├── .gitattributes ├── LICENSE ├── README.md ├── convert_data.py ├── convert_tfrecords.py ├── datasets ├── __init__.py ├── cifar10.py ├── cifar100.py ├── dataset_factory.py ├── images_to_tfrecords.py ├── svhn.py └── utils.py ├── memory.py ├── nets ├── __init__.py ├── convnet.py └── nets_factory.py ├── network.py ├── preprocessing ├── __init__.py ├── cifar_preprocess.py ├── preprocessing_factory.py ├── svhn_preprocess.py └── utils.py ├── scripts ├── convert_images_to_tfrecords.sh ├── download_prepare_datasets.sh ├── train_cifar100_semi.sh ├── train_cifar10_semi.sh └── train_svhn_semi.sh ├── semi_learner.py ├── sup_learner.py ├── test.py ├── train.py └── utils.py /.gitattributes: -------------------------------------------------------------------------------- 1 | # Auto detect text files and perform LF normalization 2 | * text=auto 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2018 Yanbei Chen 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # semi-memory 2 | 3 | [Tensorflow](https://www.tensorflow.org/) Implementation of the paper Chen et al. [**Semi-Supervised Deep Learning with Memory, ECCV2018**](http://openaccess.thecvf.com/content_ECCV_2018/papers/Yanbei_Chen_Semi-Supervised_Deep_Learning_ECCV_2018_paper.pdf). 4 | 5 | 6 | ## Getting Started 7 | 8 | ### Prerequisite: 9 | 10 | - Python 2.7. 11 | - Tensorflow version >= 1.4.0. 12 | 13 | ### Data preparation: 14 | 15 | 1. Download and prepare datasets: 16 | 17 | ``` 18 | bash scripts/download_prepare_datasets.sh 19 | ``` 20 | 21 | 2. Convert image data to tfrecords: 22 | 23 | ``` 24 | bash scripts/convert_images_to_tfrecords.sh 25 | ``` 26 | 27 | 28 | ## Running Experiments 29 | 30 | ### Training & Testing: 31 | 32 | For example, to train and test on `svhn`, run the following command. 33 | ``` 34 | bash scripts/train_svhn_semi.sh 35 | ``` 36 | 37 | 38 | ## Citation 39 | 40 | Please refer to the following if this repository is useful for your research. 41 | 42 | ### Bibtex: 43 | 44 | ``` 45 | @inproceedings{chen2018semi, 46 | title={Semi-Supervised Deep Learning with Memory}, 47 | author={Chen, Yanbei and Zhu, Xiatian and Gong, Shaogang}, 48 | booktitle={Proceedings of the European Conference on Computer Vision (ECCV)}, 49 | year={2018} 50 | } 51 | ``` 52 | 53 | ## License 54 | 55 | This project is licensed under the MIT License - see the [LICENSE.md](LICENSE.md) file for details. 56 | 57 | -------------------------------------------------------------------------------- /convert_data.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | import numpy as np 7 | import scipy.io as sio 8 | import os 9 | from PIL import Image 10 | 11 | tf.app.flags.DEFINE_string('dataset_name', None, 'The name of the dataset.') 12 | tf.app.flags.DEFINE_string('dataset_type', None, 'The type of the dataset: "train" or "test".') 13 | 14 | FLAGS = tf.app.flags.FLAGS 15 | 16 | 17 | def make_subfolder(writepath, num_classes): 18 | for i in range(num_classes): 19 | classpath = os.path.join(writepath, 'class'+str(i)) 20 | if not os.path.exists(classpath): 21 | os.makedirs(classpath) 22 | 23 | 24 | def mat_to_image_cifar(data, labels, num_classes, writepath): 25 | img_count = np.zeros([num_classes]) 26 | for i in range(labels.shape[0]): 27 | R = data[i, :1024] 28 | G = data[i, 1024:2048] 29 | B = data[i, 2048:] 30 | img = np.zeros([32, 32, 3]) 31 | k = 0 32 | for p in range(32): 33 | for q in range(32): 34 | img[p, q, 0] = R[k] 35 | img[p, q, 1] = G[k] 36 | img[p, q, 2] = B[k] 37 | k += 1 38 | img = img.astype(np.uint8) 39 | classpath = 'class' + str(labels[i]) 40 | imgpath = '{0:05}.png'.format(int(img_count[labels[i]])) 41 | imgpath = os.path.join(writepath, classpath, imgpath) 42 | print(imgpath) 43 | img = Image.fromarray(img) 44 | img.save(imgpath) 45 | img_count[labels[i]] += 1 46 | 47 | 48 | def mat_to_image_svhn(data, labels, num_classes, writepath): 49 | img_count = np.zeros([num_classes]) 50 | labels = labels-1 51 | for i in range(labels.shape[0]): 52 | img = data[:,:,:,i].astype(np.uint8) 53 | classpath = 'class' + str(labels[i]) 54 | imgpath = '{0:05}.png'.format(int(img_count[labels[i]])) 55 | imgpath = os.path.join(writepath, classpath, imgpath) 56 | print(imgpath) 57 | img = Image.fromarray(img) 58 | img.save(imgpath) 59 | img_count[labels[i]] += 1 60 | 61 | 62 | def convert_cifar100(readpath, writepath): 63 | make_subfolder(writepath, num_classes=100) 64 | filename = readpath + FLAGS.dataset_type + '.mat' 65 | rawdata = sio.loadmat(filename) 66 | data = rawdata['data'] 67 | labels = np.squeeze(rawdata['fine_labels']) 68 | mat_to_image_cifar(data, labels, num_classes=100, writepath=writepath) 69 | 70 | 71 | def convert_cifar10(readpath, writepath): 72 | make_subfolder(writepath, num_classes=10) 73 | if FLAGS.dataset_type=='test': 74 | filename = readpath + 'test_batch.mat' 75 | rawdata = sio.loadmat(filename) 76 | data = rawdata['data'] 77 | labels = np.squeeze(rawdata['labels']) 78 | mat_to_image_cifar(data, labels, num_classes=10, writepath=writepath) 79 | else: 80 | data = [] 81 | labels = [] 82 | for i in range(5): 83 | filename = readpath + 'data_batch_' + str(i+1) + '.mat' 84 | rawdata = sio.loadmat(filename) 85 | data.append(rawdata['data']) 86 | labels.append(np.squeeze(rawdata['labels'])) 87 | data = np.concatenate(data, 0) 88 | labels = np.concatenate(labels, 0) 89 | mat_to_image_cifar(data, labels, num_classes=10, writepath=writepath) 90 | 91 | 92 | def convert_svhn(readpath, writepath): 93 | make_subfolder(writepath, num_classes=10) 94 | filename = readpath + FLAGS.dataset_type + '_32x32.mat' 95 | rawdata = sio.loadmat(filename) 96 | data = rawdata['X'] 97 | labels = np.squeeze(rawdata['y']) 98 | mat_to_image_svhn(data, labels, num_classes=10, writepath=writepath) 99 | 100 | 101 | def main(): 102 | if FLAGS.dataset_name == 'svhn': 103 | readpath = 'rawdata/svhn/' 104 | writepath = os.path.join('rawdata/svhn/', FLAGS.dataset_type) 105 | convert_svhn(readpath, writepath) 106 | elif FLAGS.dataset_name == 'cifar100': 107 | readpath = 'rawdata/cifar100/cifar-100-matlab/' 108 | writepath = os.path.join('rawdata/cifar100/', FLAGS.dataset_type) 109 | convert_cifar100(readpath, writepath) 110 | elif FLAGS.dataset_name == 'cifar10': 111 | readpath = 'rawdata/cifar10/cifar-10-batches-mat/' 112 | writepath = os.path.join('rawdata/cifar10/', FLAGS.dataset_type) 113 | convert_cifar10(readpath, writepath) 114 | else: 115 | raise ValueError('You must supply the dataset name as -- svhn, cifar100, or cifar10') 116 | 117 | 118 | if __name__ == "__main__": 119 | main() 120 | -------------------------------------------------------------------------------- /convert_tfrecords.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | from datasets import images_to_tfrecords 7 | 8 | tf.app.flags.DEFINE_string('dataset_dir', None, 'The dir of the raw image data.') 9 | tf.app.flags.DEFINE_string('split_name', None, 'Either "train" or "test".') 10 | tf.app.flags.DEFINE_string('dataset_name', None, 'The name of the dataset to convert.') 11 | tf.app.flags.DEFINE_string('output_dir', None, 'The dir of the output data.') 12 | tf.app.flags.DEFINE_integer('num_splits', None, 'The number of datasplits.') 13 | tf.app.flags.DEFINE_integer('labels_per_class', None, 'The number of labeled sample per class.') 14 | 15 | FLAGS = tf.app.flags.FLAGS 16 | 17 | 18 | def main(_): 19 | if not FLAGS.dataset_name: 20 | raise ValueError('You must supply the dataset name with --dataset_name') 21 | elif FLAGS.dataset_name in {'svhn','cifar10', 'cifar100'}: 22 | images_to_tfrecords.run(FLAGS.dataset_dir, 23 | FLAGS.split_name, 24 | FLAGS.dataset_name, 25 | FLAGS.output_dir, 26 | FLAGS.num_splits, 27 | FLAGS.labels_per_class) 28 | else: 29 | raise ValueError('dataset_name [%s] was not recognized.' % FLAGS.dataset_dir) 30 | 31 | 32 | if __name__ == '__main__': 33 | tf.app.run() 34 | -------------------------------------------------------------------------------- /datasets/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanbeic/semi-memory/e510aa377161a04d4630c857c8fb247d6a500fb3/datasets/__init__.py -------------------------------------------------------------------------------- /datasets/cifar10.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import os 6 | import tensorflow as tf 7 | import tensorflow.contrib.slim as slim 8 | 9 | _FILE_PATTERN = 'cifar10_%s.tfrecord' 10 | 11 | SPLITS_TO_SIZES = {'train': 50000, 'test': 10000} 12 | 13 | _NUM_CLASSES = 10 14 | 15 | _ITEMS_TO_DESCRIPTIONS = \ 16 | { 17 | 'image': 'A [32 x 32 x 3] color image.', 18 | 'label': 'A single integer between 0 and 9', 19 | } 20 | 21 | 22 | def get_split(split_name, dataset_dir, file_pattern=None, reader=None): 23 | 24 | if split_name not in SPLITS_TO_SIZES: 25 | raise ValueError('split name %s was not recognized.' % split_name) 26 | 27 | if not file_pattern: 28 | file_pattern = _FILE_PATTERN 29 | file_pattern = os.path.join(dataset_dir, file_pattern % split_name) 30 | 31 | if not reader: 32 | reader = tf.TFRecordReader 33 | 34 | keys_to_features = \ 35 | { 36 | 'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''), 37 | 'image/format': tf.FixedLenFeature((), tf.string, default_value='png'), 38 | 'image/class/label': tf.FixedLenFeature([], tf.int64, default_value=tf.zeros([], dtype=tf.int64)), 39 | } 40 | 41 | items_to_handlers = \ 42 | { 43 | 'image': slim.tfexample_decoder.Image(shape=[32, 32, 3]), 44 | 'label': slim.tfexample_decoder.Tensor('image/class/label'), 45 | } 46 | 47 | decoder = slim.tfexample_decoder.TFExampleDecoder(keys_to_features, items_to_handlers) 48 | 49 | return slim.dataset.Dataset( 50 | data_sources=file_pattern, 51 | reader=reader, 52 | decoder=decoder, 53 | num_samples=SPLITS_TO_SIZES[split_name], 54 | items_to_descriptions=_ITEMS_TO_DESCRIPTIONS, 55 | num_classes=_NUM_CLASSES) 56 | -------------------------------------------------------------------------------- /datasets/cifar100.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import os 6 | import tensorflow as tf 7 | import tensorflow.contrib.slim as slim 8 | 9 | _FILE_PATTERN = 'cifar100_%s.tfrecord' 10 | 11 | SPLITS_TO_SIZES = {'train': 50000, 'test': 10000} 12 | 13 | _NUM_CLASSES = 100 14 | 15 | _ITEMS_TO_DESCRIPTIONS = \ 16 | { 17 | 'image': 'A [32 x 32 x 3] color image.', 18 | 'label': 'A single integer between 0 and 99', 19 | } 20 | 21 | 22 | def get_split(split_name, dataset_dir, file_pattern=None, reader=None): 23 | 24 | if split_name not in SPLITS_TO_SIZES: 25 | raise ValueError('split name %s was not recognized.' % split_name) 26 | 27 | if not file_pattern: 28 | file_pattern = _FILE_PATTERN 29 | file_pattern = os.path.join(dataset_dir, file_pattern % split_name) 30 | 31 | if not reader: 32 | reader = tf.TFRecordReader 33 | 34 | keys_to_features = \ 35 | { 36 | 'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''), 37 | 'image/format': tf.FixedLenFeature((), tf.string, default_value='png'), 38 | 'image/class/label': tf.FixedLenFeature([], tf.int64, default_value=tf.zeros([], dtype=tf.int64)), 39 | } 40 | 41 | items_to_handlers = \ 42 | { 43 | 'image': slim.tfexample_decoder.Image(shape=[32, 32, 3]), 44 | 'label': slim.tfexample_decoder.Tensor('image/class/label'), 45 | } 46 | 47 | decoder = slim.tfexample_decoder.TFExampleDecoder(keys_to_features, items_to_handlers) 48 | 49 | return slim.dataset.Dataset( 50 | data_sources=file_pattern, 51 | reader=reader, 52 | decoder=decoder, 53 | num_samples=SPLITS_TO_SIZES[split_name], 54 | items_to_descriptions=_ITEMS_TO_DESCRIPTIONS, 55 | num_classes=_NUM_CLASSES) 56 | 57 | 58 | -------------------------------------------------------------------------------- /datasets/dataset_factory.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | from datasets import svhn 7 | from datasets import cifar10 8 | from datasets import cifar100 9 | 10 | FLAGS = tf.app.flags.FLAGS 11 | 12 | 13 | datasets_map = \ 14 | { 15 | 'svhn': svhn, 16 | 'cifar10': cifar10, 17 | 'cifar100': cifar100, 18 | } 19 | 20 | 21 | def get_dataset(name, split_name, dataset_dir, file_pattern=None, reader=None): 22 | if name not in datasets_map: 23 | raise ValueError('Name of dataset unknown %s' % name) 24 | return datasets_map[name].get_split( 25 | split_name, 26 | dataset_dir, 27 | file_pattern, 28 | reader) 29 | -------------------------------------------------------------------------------- /datasets/images_to_tfrecords.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import os 6 | import sys 7 | import random 8 | import numpy as np 9 | import tensorflow as tf 10 | from PIL import Image 11 | from datasets import utils 12 | 13 | # The height and width of each image. 14 | _IMAGE_SIZE = 32 15 | 16 | 17 | def _write_to_tfrecord(filenames, labels, tfrecord_writer): 18 | num_images = len(filenames) 19 | with tf.Graph().as_default(): 20 | image_placeholder = tf.placeholder(dtype=tf.uint8) 21 | encoded_image = tf.image.encode_png(image_placeholder) 22 | with tf.Session('') as sess: 23 | for i in range(num_images): 24 | sys.stdout.write('\r>> Reading images %d/%d' % (i + 1, num_images)) 25 | sys.stdout.flush() 26 | image_path = filenames[i] 27 | image = Image.open(image_path) 28 | label = labels[i] 29 | png_string = sess.run(encoded_image, feed_dict={image_placeholder: image}) 30 | example = utils.image_to_tfexample(png_string, 'png', _IMAGE_SIZE, _IMAGE_SIZE, label) 31 | tfrecord_writer.write(example.SerializeToString()) 32 | 33 | 34 | def _get_testdata_filenames_and_labels(dataset_dir): 35 | classes = os.listdir(dataset_dir) 36 | filenames = [] 37 | labels = [] 38 | for i in range(len(classes)): 39 | classpath = os.path.join(os.getcwd(), dataset_dir, classes[i]) 40 | imgpaths = os.listdir(classpath) 41 | imgpaths = [os.path.join(classpath, substring) for substring in imgpaths] 42 | filenames.append(imgpaths) 43 | labels.append(i * np.ones(len(imgpaths), dtype=np.int64)) 44 | filenames = np.concatenate(filenames, 0) 45 | labels = np.concatenate(labels, 0) 46 | return filenames, labels 47 | 48 | 49 | def _shuffle_data(filenames, labels): 50 | shuffled_index = list(range(len(filenames))) 51 | random.seed(12345) 52 | random.shuffle(shuffled_index) 53 | filenames = [filenames[i] for i in shuffled_index] 54 | labels = [labels[i] for i in shuffled_index] 55 | return filenames, labels 56 | 57 | 58 | def _get_traindata_filenames_and_labels(dataset_dir, labels_per_class): 59 | classes = os.listdir(dataset_dir) 60 | filenames_l = [] 61 | filenames_u = [] 62 | labels_l = [] 63 | labels_u = [] 64 | for i in range(len(classes)): 65 | classpath = os.path.join(os.getcwd(), dataset_dir, classes[i]) 66 | imgpaths = os.listdir(classpath) 67 | imgpaths = [os.path.join(classpath, substring) for substring in imgpaths] 68 | random.seed(12345) 69 | random.shuffle(imgpaths) 70 | imgpaths_l = imgpaths[:labels_per_class] 71 | imgpaths_u = imgpaths[labels_per_class:] 72 | filenames_l.append(imgpaths_l) 73 | filenames_u.append(imgpaths_u) 74 | labels_l.append(i * np.ones(labels_per_class, dtype=np.int64)) 75 | labels_u.append(-1 * np.ones(len(imgpaths) - labels_per_class, dtype=np.int64)) 76 | filenames_l = np.concatenate(filenames_l, 0) 77 | filenames_u = np.concatenate(filenames_u, 0) 78 | labels_l = np.concatenate(labels_l, 0) 79 | labels_u = np.concatenate(labels_u, 0) 80 | # shuffle traindata completely 81 | filenames_l, labels_l = _shuffle_data(filenames_l, labels_l) 82 | filenames_u, labels_u = _shuffle_data(filenames_u, labels_u) 83 | return filenames_l, labels_l, filenames_u, labels_u 84 | 85 | 86 | def _make_dir(output_dir): 87 | if not os.path.exists(output_dir): 88 | os.makedirs(output_dir) 89 | 90 | 91 | def _get_tfrecords_names(output_dir, dataset_name, split_name): 92 | tfrecords_names = '%s/%s_%s.tfrecord' % (output_dir, dataset_name, split_name) 93 | _make_dir(output_dir) 94 | return tfrecords_names 95 | 96 | 97 | def run(dataset_dir, split_name, dataset_name, output_dir, num_splits, labels_per_class): 98 | 99 | if split_name == 'test': 100 | tfrecords_names = _get_tfrecords_names(output_dir, dataset_name, split_name) 101 | filenames, labels = _get_testdata_filenames_and_labels(dataset_dir) 102 | with tf.python_io.TFRecordWriter(tfrecords_names) as tfrecord_writer: 103 | _write_to_tfrecord(filenames, labels, tfrecord_writer) 104 | else: 105 | for i in range(num_splits): 106 | output_sub_dir_l = os.path.join(output_dir, 'split'+str(i+1), 'labeled') 107 | tfrecords_names_l = _get_tfrecords_names(output_sub_dir_l, dataset_name, split_name) 108 | 109 | output_sub_dir_u = os.path.join(output_dir, 'split'+str(i+1), 'unlabeled') 110 | tfrecords_names_u = _get_tfrecords_names(output_sub_dir_u, dataset_name, split_name) 111 | 112 | filenames_l, labels_l, filenames_u, labels_u = \ 113 | _get_traindata_filenames_and_labels(dataset_dir, labels_per_class) 114 | 115 | print('\nConverting the labelled data.') 116 | with tf.python_io.TFRecordWriter(tfrecords_names_l) as tfrecord_writer: 117 | _write_to_tfrecord(filenames_l, labels_l, tfrecord_writer) 118 | 119 | print('\nConverting the unlabelled data.') 120 | with tf.python_io.TFRecordWriter(tfrecords_names_u) as tfrecord_writer: 121 | _write_to_tfrecord(filenames_u, labels_u, tfrecord_writer) 122 | 123 | 124 | print('\nFinished converting the data.') 125 | -------------------------------------------------------------------------------- /datasets/svhn.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import os 6 | import tensorflow as tf 7 | import tensorflow.contrib.slim as slim 8 | 9 | _FILE_PATTERN = 'svhn_%s.tfrecord' 10 | 11 | SPLITS_TO_SIZES = {'train': 73257, 'test': 26032} 12 | 13 | _NUM_CLASSES = 10 14 | 15 | _ITEMS_TO_DESCRIPTIONS = \ 16 | { 17 | 'image': 'A [32 x 32 x 3] color image.', 18 | 'label': 'A single integer between 0 and 9', 19 | } 20 | 21 | 22 | def get_split(split_name, dataset_dir, file_pattern=None, reader=None): 23 | 24 | if split_name not in SPLITS_TO_SIZES: 25 | raise ValueError('split name %s was not recognized.' % split_name) 26 | 27 | if not file_pattern: 28 | file_pattern = _FILE_PATTERN 29 | file_pattern = os.path.join(dataset_dir, file_pattern % split_name) 30 | 31 | if not reader: 32 | reader = tf.TFRecordReader 33 | 34 | keys_to_features = \ 35 | { 36 | 'image/encoded': tf.FixedLenFeature((), tf.string, default_value=''), 37 | 'image/format': tf.FixedLenFeature((), tf.string, default_value='png'), 38 | 'image/class/label': tf.FixedLenFeature([], tf.int64, default_value=tf.zeros([], dtype=tf.int64)), 39 | } 40 | 41 | items_to_handlers = \ 42 | { 43 | 'image': slim.tfexample_decoder.Image(shape=[32, 32, 3]), 44 | 'label': slim.tfexample_decoder.Tensor('image/class/label'), 45 | } 46 | 47 | decoder = slim.tfexample_decoder.TFExampleDecoder(keys_to_features, items_to_handlers) 48 | 49 | return slim.dataset.Dataset( 50 | data_sources=file_pattern, 51 | reader=reader, 52 | decoder=decoder, 53 | num_samples=SPLITS_TO_SIZES[split_name], 54 | items_to_descriptions=_ITEMS_TO_DESCRIPTIONS, 55 | num_classes=_NUM_CLASSES) 56 | -------------------------------------------------------------------------------- /datasets/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | 7 | 8 | def int64_feature(values): 9 | if not isinstance(values, (tuple, list)): 10 | values = [values] 11 | return tf.train.Feature(int64_list=tf.train.Int64List(value=values)) 12 | 13 | 14 | def bytes_feature(values): 15 | return tf.train.Feature(bytes_list=tf.train.BytesList(value=[values])) 16 | 17 | 18 | def image_to_tfexample(image_data, image_format, height, width, label): 19 | return tf.train.Example(features=tf.train.Features(feature={ 20 | 'image/encoded': bytes_feature(image_data), 21 | 'image/format': bytes_feature(image_format), 22 | 'image/class/label': int64_feature(label), 23 | 'image/height': int64_feature(height), 24 | 'image/width': int64_feature(width), 25 | })) 26 | -------------------------------------------------------------------------------- /memory.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | import network 7 | 8 | FLAGS = tf.app.flags.FLAGS 9 | EPISILON=1e-10 10 | 11 | mask = lambda x, y: tf.boolean_mask(x, y) 12 | diff = lambda x, n, eta: (x / tf.cast((1 + n), tf.float32))*eta 13 | normalize = lambda x: x / tf.reduce_sum(x, axis=1, keep_dims=True) 14 | 15 | 16 | def module(reuse_variables, labels, features, inferences): 17 | num_c = FLAGS.num_classes 18 | dim_f = FLAGS.feature_dim 19 | with tf.variable_scope("memory", reuse=reuse_variables): 20 | keys = tf.get_variable('keys', 21 | shape=[num_c, dim_f], 22 | dtype=tf.float32, 23 | initializer=tf.constant_initializer(0.0), 24 | trainable=False) 25 | values = tf.get_variable('values', 26 | shape=[num_c, num_c], 27 | dtype=tf.float32, 28 | initializer=tf.constant_initializer(1.0 / float(num_c)), 29 | trainable=False) 30 | 31 | diff_key = tf.gather(keys, labels) - features 32 | diff_value = tf.gather(values, labels) - inferences 33 | 34 | y, idx, count = tf.unique_with_counts(labels) 35 | count_n = tf.expand_dims(tf.gather(count, idx), 1) 36 | 37 | diff_key = diff(diff_key, count_n, FLAGS.eta) 38 | diff_value = diff(diff_value, count_n, FLAGS.eta) 39 | 40 | keys = tf.scatter_sub(keys, labels, diff_key) 41 | values = normalize(tf.scatter_sub(values, labels, diff_value)) 42 | 43 | return keys, values 44 | 45 | 46 | def label_ulabel(labels, logits, features): 47 | where_label = tf.not_equal(labels, -1) # unlabel is given as -1 48 | where_unlabel = tf.equal(labels, -1) 49 | labels_l = mask(labels, where_label) 50 | logits_l = mask(logits, where_label) 51 | logits_u = mask(logits, where_unlabel) 52 | features_l = mask(features, where_label) 53 | features_u = mask(features, where_unlabel) 54 | return labels_l, logits_l, logits_u, features_l, features_u 55 | 56 | 57 | def content_based(keys, values, features_u): 58 | dist = tf.sqrt((features_u[:, tf.newaxis] - keys) ** 2 + EPISILON) 59 | memberships = tf.nn.softmax(-tf.reduce_sum(dist, axis=2)) 60 | memberships = tf.clip_by_value(memberships, EPISILON, 1) 61 | pred_u = normalize(tf.reduce_sum(memberships[:, tf.newaxis] * values, 2)) 62 | return pred_u 63 | 64 | 65 | def position_based(values, labels_l): 66 | pred_l = tf.gather(values, labels_l) 67 | return pred_l 68 | 69 | 70 | def memory_prediction(keys, values, labels_l, features_u): 71 | # key addressing & value reading 72 | pred_l = position_based(values, labels_l) 73 | pred_u = content_based(keys, values, features_u) 74 | return tf.concat([pred_l, pred_u], 0, name='memory_pred') 75 | 76 | 77 | def assimilation(keys, values, labels_l, features_u, logits): 78 | mem_pred = memory_prediction(keys, values, labels_l, features_u) 79 | net_pred = tf.nn.softmax(logits) 80 | return mem_pred, net_pred 81 | 82 | 83 | def accomodation(mem_pred, net_pred): 84 | # model entropy 85 | m_entropy = network.loss_entropy(mem_pred) 86 | # memory-network divergence 87 | mn_divergence = network.loss_kl(net_pred, mem_pred) 88 | uncertainty = tf.reduce_max(mem_pred, axis=1) 89 | mnd_weighted = tf.multiply(mn_divergence, uncertainty) 90 | loss_m = tf.reduce_mean(tf.add(m_entropy, mnd_weighted)) 91 | return loss_m 92 | -------------------------------------------------------------------------------- /nets/__init__.py: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /nets/convnet.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | from collections import namedtuple 6 | import tensorflow as tf 7 | import tensorflow.contrib.slim as slim 8 | 9 | Conv = namedtuple('Conv', ['kernel', 'stride', 'depth', 'padding']) 10 | MaxPool = namedtuple('MaxPool',['kernel', 'stride']) 11 | 12 | _CONV = [ 13 | Conv(kernel=[3, 3], stride=1, depth=128, padding='SAME'), 14 | Conv(kernel=[3, 3], stride=1, depth=128, padding='SAME'), 15 | Conv(kernel=[3, 3], stride=1, depth=128, padding='SAME'), 16 | MaxPool(kernel=[2, 2], stride=[2, 2]), 17 | Conv(kernel=[3, 3], stride=1, depth=256, padding='SAME'), 18 | Conv(kernel=[3, 3], stride=1, depth=256, padding='SAME'), 19 | Conv(kernel=[3, 3], stride=1, depth=256, padding='SAME'), 20 | MaxPool(kernel=[2,2], stride=[2, 2]), 21 | Conv(kernel=[3, 3], stride=1, depth=512, padding='VALID'), 22 | Conv(kernel=[1, 1], stride=1, depth=256, padding='SAME'), 23 | Conv(kernel=[1, 1], stride=1, depth=128, padding='SAME'), 24 | ] 25 | 26 | 27 | def lrelu(inputs, leak=0.1, name="lrelu"): 28 | with tf.name_scope(name, 'lrelu') as scope: 29 | return tf.maximum(inputs, leak*inputs, name=scope) 30 | 31 | 32 | def net_base(inputs, final_endpoint='Conv2d_10', is_training=True, scope=None): 33 | end_points = {} 34 | with tf.variable_scope(scope, 'MobilenetV1', [inputs]): 35 | with slim.arg_scope([slim.conv2d]): 36 | net = inputs 37 | print('base net architectures & layer outputs') 38 | for i, conv_def in enumerate(_CONV): 39 | end_point_base = 'Conv2d_%d' % i 40 | 41 | if isinstance(conv_def, Conv): 42 | end_point = end_point_base 43 | net = slim.conv2d(inputs=net, 44 | num_outputs=conv_def.depth, 45 | kernel_size=conv_def.kernel, 46 | stride=conv_def.stride, 47 | padding=conv_def.padding, 48 | activation_fn=lrelu, 49 | normalizer_fn=slim.batch_norm, 50 | scope=end_point) 51 | end_points[end_point] = net 52 | if end_point == final_endpoint: 53 | return net, end_points 54 | 55 | elif isinstance(conv_def, MaxPool): 56 | end_point = end_point_base + '_MaxPool' 57 | net = slim.max_pool2d(inputs=net, 58 | kernel_size=conv_def.kernel, 59 | stride=conv_def.stride, 60 | padding='SAME', 61 | scope=end_point) 62 | net = slim.dropout(inputs=net, 63 | keep_prob=0.5, 64 | is_training=is_training, 65 | scope=end_point + '_Dropout') 66 | end_points[end_point] = net 67 | if end_point == final_endpoint: 68 | return net, end_points 69 | else: 70 | raise ValueError('Unknown convolution type %s for layer %d' 71 | % (conv_def.ltype, i)) 72 | print(conv_def) 73 | print(net) 74 | raise ValueError('Unknown final endpoint %s' % final_endpoint) 75 | 76 | 77 | def convnet_large(inputs, num_classes=1000, is_training=True, prediction_fn=tf.contrib.layers.softmax, 78 | reuse=None, scope='convnet'): 79 | 80 | input_shape = inputs.get_shape().as_list() 81 | if len(input_shape) != 4: 82 | raise ValueError('Invalid input tensor rank, expected 4, was: %d' % len(input_shape)) 83 | 84 | with tf.variable_scope(scope, 'convnet', [inputs, num_classes], reuse=reuse) as scope: 85 | with slim.arg_scope([slim.batch_norm, slim.dropout], is_training=is_training): 86 | net, end_points = net_base(inputs=inputs, is_training=is_training, scope=scope) 87 | with tf.variable_scope('Logits'): 88 | kernel_size = _reduced_kernel_size_for_small_input(net, [6, 6]) 89 | net = slim.avg_pool2d(inputs=net, 90 | kernel_size=kernel_size, 91 | padding='VALID', 92 | scope='AvgPool') 93 | end_points['AvgPool'] = net 94 | logits = slim.conv2d(inputs=net, 95 | num_outputs=num_classes, 96 | kernel_size=[1, 1], 97 | activation_fn=None, 98 | normalizer_fn=None, 99 | scope='Conv2d_1c_1x1') 100 | logits = tf.squeeze(logits, [1, 2], name='SpatialSqueeze') 101 | end_points['Logits'] = logits 102 | if prediction_fn: 103 | end_points['Predictions'] = prediction_fn(logits, scope='Predictions') 104 | return logits, end_points 105 | 106 | 107 | convnet_large.default_image_size = 32 108 | 109 | 110 | def _reduced_kernel_size_for_small_input(input_tensor, kernel_size): 111 | shape = input_tensor.get_shape().as_list() 112 | if shape[1] is None or shape[2] is None: 113 | kernel_size_out = kernel_size 114 | else: 115 | kernel_size_out = [min(shape[1], kernel_size[0]), 116 | min(shape[2], kernel_size[1])] 117 | return kernel_size_out 118 | 119 | 120 | def convnet_arg_scope(is_training=True, weight_decay=0.00005, stddev=0.05): 121 | batch_norm_params = { 122 | 'is_training': is_training, 123 | 'center': True, 124 | 'scale': True, 125 | 'decay': 0.9999, 126 | 'epsilon': 0.001, 127 | 'zero_debias_moving_mean': True, 128 | } 129 | weights_init = tf.random_normal_initializer(0, stddev) 130 | regularizer = tf.contrib.layers.l2_regularizer(weight_decay) 131 | with slim.arg_scope([slim.conv2d], weights_initializer=weights_init, 132 | activation_fn=lrelu, normalizer_fn=slim.batch_norm): 133 | with slim.arg_scope([slim.batch_norm], **batch_norm_params): 134 | with slim.arg_scope([slim.conv2d], weights_regularizer=regularizer) as sc: 135 | return sc 136 | -------------------------------------------------------------------------------- /nets/nets_factory.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import functools 6 | import tensorflow as tf 7 | from nets import convnet 8 | 9 | slim = tf.contrib.slim 10 | 11 | networks_map = \ 12 | { 13 | 'convnet': convnet.convnet_large, 14 | } 15 | 16 | arg_scopes_map = \ 17 | { 18 | 'convnet': convnet.convnet_arg_scope, 19 | } 20 | 21 | 22 | def get_network_fn(name, num_classes, weight_decay=0.0, is_training=False): 23 | 24 | if name not in networks_map: 25 | raise ValueError('Name of network unknown %s' % name) 26 | arg_scope = arg_scopes_map[name](weight_decay=weight_decay) 27 | func = networks_map[name] 28 | 29 | @functools.wraps(func) 30 | def network_fn(images): 31 | with slim.arg_scope(arg_scope): 32 | return func(images, num_classes, is_training=is_training) 33 | 34 | if hasattr(func, 'default_image_size'): 35 | network_fn.default_image_size = func.default_image_size 36 | if hasattr(func, 'default_image_height'): 37 | network_fn.default_image_height = func.default_image_height 38 | if hasattr(func, 'default_image_width'): 39 | network_fn.default_image_width = func.default_image_width 40 | 41 | return network_fn 42 | -------------------------------------------------------------------------------- /network.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | import tensorflow.contrib.slim as slim 7 | from nets import nets_factory 8 | 9 | FLAGS = tf.app.flags.FLAGS 10 | TOWER_NAME = 'tower' 11 | EPISILON = 1e-8 12 | 13 | 14 | def inference(images, num_classes, for_training=False, feature_name=None): 15 | network_fn = nets_factory.get_network_fn( 16 | FLAGS.model_name, 17 | num_classes=num_classes, 18 | weight_decay=FLAGS.weight_decay, 19 | is_training=for_training) 20 | logits, end_points = network_fn(images) 21 | features = tf.squeeze(tf.squeeze(end_points[feature_name], squeeze_dims=1), squeeze_dims=1) 22 | return logits, features 23 | 24 | 25 | def loss_ce(logits, labels, weight=1.0): 26 | labels = slim.one_hot_encoding(labels, FLAGS.num_classes) 27 | return tf.cond(tf.size(labels) > 0, 28 | lambda: tf.losses.softmax_cross_entropy( 29 | logits=logits, 30 | onehot_labels=labels, 31 | label_smoothing=FLAGS.smoothing*float(FLAGS.num_classes), 32 | weights=weight), 33 | lambda: tf.constant(0.0)) 34 | 35 | 36 | def loss_entropy(p_prob, weight=1.0): 37 | entropy = -tf.multiply(p_prob, tf.log(p_prob+EPISILON)) 38 | return tf.multiply(weight, tf.reduce_sum(entropy, axis=1), name='entropy') 39 | 40 | 41 | def loss_kl(p_prob, q_prob, weight=1.0): 42 | KL_divergence = tf.multiply(p_prob, tf.log(tf.divide(p_prob, q_prob) + EPISILON)) 43 | return tf.multiply(weight, tf.reduce_sum(KL_divergence, axis=1), name='kl') 44 | 45 | 46 | def average_gradients(tower_grads): 47 | average_grads = [] 48 | for grad_and_vars in zip(*tower_grads): 49 | grads = [] 50 | for g, _ in grad_and_vars: 51 | expanded_g = tf.expand_dims(g, 0) 52 | grads.append(expanded_g) 53 | grad = tf.concat(grads, 0) 54 | grad = tf.reduce_mean(grad, 0) 55 | grad = tf.clip_by_value(grad, -2., 2.) 56 | v = grad_and_vars[0][1] 57 | grad_and_var = (grad, v) 58 | average_grads.append(grad_and_var) 59 | return average_grads 60 | -------------------------------------------------------------------------------- /preprocessing/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/yanbeic/semi-memory/e510aa377161a04d4630c857c8fb247d6a500fb3/preprocessing/__init__.py -------------------------------------------------------------------------------- /preprocessing/cifar_preprocess.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | from preprocessing import utils 7 | 8 | _PADDING = 2 9 | _IMAGE_SIZE = 32 10 | 11 | 12 | def preprocess_for_train(image, height=_IMAGE_SIZE, width=_IMAGE_SIZE, padding=_PADDING): 13 | image = tf.image.convert_image_dtype(image, dtype=tf.float32) 14 | image = utils.flip(image) 15 | image = utils.translation(image, height, width, padding) 16 | image = utils.distort_color(image) 17 | image = utils.clip(image) 18 | return image 19 | 20 | 21 | def preprocess_for_eval(image, height=_IMAGE_SIZE, width=_IMAGE_SIZE): 22 | image = tf.image.convert_image_dtype(image, dtype=tf.float32) 23 | image = tf.image.resize_image_with_crop_or_pad(image, height, width) 24 | return image 25 | 26 | 27 | def preprocess_image(image, height, width, is_training=False): 28 | if is_training: 29 | return preprocess_for_train(image, height, width) 30 | else: 31 | return preprocess_for_eval(image, height, width) 32 | -------------------------------------------------------------------------------- /preprocessing/preprocessing_factory.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | from preprocessing import cifar_preprocess 6 | from preprocessing import svhn_preprocess 7 | 8 | 9 | def get_preprocessing(name, is_training=False): 10 | 11 | preprocessing_fn_map = \ 12 | { 13 | 'cifar': cifar_preprocess, 14 | 'svhn': svhn_preprocess, 15 | } 16 | 17 | if name not in preprocessing_fn_map: 18 | raise ValueError('Preprocessing name [%s] was not recognized' % name) 19 | 20 | def preprocessing_fn(image, output_height, output_width): 21 | return preprocessing_fn_map[name].preprocess_image( 22 | image, output_height, output_width, is_training=is_training) 23 | 24 | return preprocessing_fn 25 | -------------------------------------------------------------------------------- /preprocessing/svhn_preprocess.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | from preprocessing import utils 7 | 8 | _PADDING = 2 9 | _IMAGE_SIZE = 32 10 | 11 | 12 | def preprocess_for_train(image, height=_IMAGE_SIZE, width=_IMAGE_SIZE, padding=_PADDING): 13 | image = tf.image.convert_image_dtype(image, dtype=tf.float32) 14 | image = utils.rotation(image) 15 | image = utils.translation(image, height, width, padding) 16 | image = utils.distort_color(image) 17 | image = utils.clip(image) 18 | return image 19 | 20 | 21 | def preprocess_for_eval(image, height=_IMAGE_SIZE, width=_IMAGE_SIZE): 22 | image = tf.image.convert_image_dtype(image, dtype=tf.float32) 23 | image = tf.image.resize_image_with_crop_or_pad(image, height, width) 24 | return image 25 | 26 | 27 | def preprocess_image(image, height, width, is_training=False): 28 | if is_training: 29 | return preprocess_for_train(image, height, width) 30 | else: 31 | return preprocess_for_eval(image, height, width) 32 | -------------------------------------------------------------------------------- /preprocessing/utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | 7 | brightness = lambda x: tf.image.random_brightness(x, max_delta=63. / 255.) 8 | saturation = lambda x: tf.image.random_saturation(x, lower=0.2, upper=1.8) 9 | contrast = lambda x: tf.image.random_contrast(x, lower=0.2, upper=1.8) 10 | 11 | 12 | def random_num(num): 13 | select_num = tf.random_uniform(shape=[], minval=1, maxval=num + 1, dtype=tf.int32) 14 | return select_num 15 | 16 | 17 | def pad(image, padding): 18 | image = tf.pad(image, [[padding, padding], [padding, padding], [0, 0]], mode='REFLECT') 19 | return image 20 | 21 | 22 | def set_shape(image, h, w): 23 | def fn1(): return tf.random_crop(image, [h, w, 3]) 24 | def fn2(): return tf.image.resize_images(image, [h, w]) 25 | image = tf.cond(tf.equal(random_num(2), 1), fn1, fn2) 26 | image.set_shape([h, w, 3]) 27 | return image 28 | 29 | 30 | def translation(image, h, w, padding): 31 | image = pad(image, padding) 32 | image = set_shape(image, h, w) 33 | return image 34 | 35 | 36 | def flip(image): 37 | image = tf.image.random_flip_left_right(image) 38 | return image 39 | 40 | 41 | def rotation(image): 42 | degree = tf.random_uniform(shape=[1], minval=-0.2, maxval=0.2) 43 | image = tf.contrib.image.rotate(image, degree, interpolation='BILINEAR') 44 | return image 45 | 46 | 47 | def distort_color(image): 48 | def fn1(): return contrast(saturation(brightness(image))) 49 | def fn2(): return saturation(contrast(brightness(image))) 50 | def fn3(): return contrast(brightness(saturation(image))) 51 | def fn4(): return brightness(contrast(saturation(image))) 52 | def fn5(): return saturation(brightness(contrast(image))) 53 | def fn6(): return brightness(saturation(contrast(image))) 54 | def fn(): return image 55 | cond = random_num(6) 56 | image = tf.cond(tf.equal(cond, 1), fn1, fn) 57 | image = tf.cond(tf.equal(cond, 2), fn2, fn) 58 | image = tf.cond(tf.equal(cond, 3), fn3, fn) 59 | image = tf.cond(tf.equal(cond, 4), fn4, fn) 60 | image = tf.cond(tf.equal(cond, 5), fn5, fn) 61 | image = tf.cond(tf.equal(cond, 6), fn6, fn) 62 | return image 63 | 64 | 65 | def clip(image): 66 | image = tf.clip_by_value(image, 0.0, 1.0) 67 | return image 68 | -------------------------------------------------------------------------------- /scripts/convert_images_to_tfrecords.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | export CUDA_VISIBLE_DEVICES= 4 | 5 | for SPLIT_NAME in {train,test} 6 | do 7 | DATA_NAME=svhn 8 | INPUT_DIR=rawdata/${DATA_NAME}/${SPLIT_NAME} 9 | OUTPUT_DIR=tfrecords/${DATA_NAME}/${SPLIT_NAME} 10 | 11 | python convert_tfrecords.py \ 12 | --dataset_dir=${INPUT_DIR} \ 13 | --split_name=${SPLIT_NAME} \ 14 | --dataset_name=${DATA_NAME} \ 15 | --output_dir=${OUTPUT_DIR} \ 16 | --num_splits=1 \ 17 | --labels_per_class=100 18 | done 19 | 20 | 21 | for SPLIT_NAME in {train,test} 22 | do 23 | DATA_NAME=cifar10 24 | INPUT_DIR=rawdata/${DATA_NAME}/${SPLIT_NAME} 25 | OUTPUT_DIR=tfrecords/${DATA_NAME}/${SPLIT_NAME} 26 | 27 | python convert_tfrecords.py \ 28 | --dataset_dir=${INPUT_DIR} \ 29 | --split_name=${SPLIT_NAME} \ 30 | --dataset_name=${DATA_NAME} \ 31 | --output_dir=${OUTPUT_DIR} \ 32 | --num_splits=1 \ 33 | --labels_per_class=400 34 | done 35 | 36 | 37 | for SPLIT_NAME in {train,test} 38 | do 39 | DATA_NAME=cifar100 40 | INPUT_DIR=rawdata/${DATA_NAME}/${SPLIT_NAME} 41 | OUTPUT_DIR=tfrecords/${DATA_NAME}/${SPLIT_NAME} 42 | 43 | python convert_tfrecords.py \ 44 | --dataset_dir=${INPUT_DIR} \ 45 | --split_name=${SPLIT_NAME} \ 46 | --dataset_name=${DATA_NAME} \ 47 | --output_dir=${OUTPUT_DIR} \ 48 | --num_splits=1 \ 49 | --labels_per_class=100 50 | done 51 | -------------------------------------------------------------------------------- /scripts/download_prepare_datasets.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | 3 | mkdir rawdata 4 | mkdir rawdata/svhn 5 | mkdir rawdata/cifar10 6 | mkdir rawdata/cifar100 7 | 8 | mkdir results 9 | mkdir results/svhn 10 | mkdir results/cifar10 11 | mkdir results/cifar100 12 | 13 | mkdir tfrecords 14 | mkdir tfrecords/svhn 15 | mkdir tfrecords/cifar10 16 | mkdir tfrecords/cifar100 17 | 18 | cd rawdata/svhn 19 | wget http://ufldl.stanford.edu/housenumbers/train_32x32.mat 20 | wget http://ufldl.stanford.edu/housenumbers/test_32x32.mat 21 | cd .. 22 | cd .. 23 | 24 | cd rawdata/cifar10 25 | wget https://www.cs.toronto.edu/~kriz/cifar-10-matlab.tar.gz 26 | tar xvzf cifar-10-matlab.tar.gz 27 | rm cifar-10-matlab.tar.gz 28 | cd .. 29 | cd .. 30 | 31 | cd rawdata/cifar100 32 | wget https://www.cs.toronto.edu/~kriz/cifar-100-matlab.tar.gz 33 | tar xvzf cifar-100-matlab.tar.gz 34 | rm cifar-100-matlab.tar.gz 35 | cd .. 36 | cd .. 37 | 38 | 39 | for DATA_NAME in {svhn,cifar10,cifar100} 40 | do 41 | for DATA_TYPE in {test,train} 42 | do 43 | python convert_data.py \ 44 | --dataset_name=${DATA_NAME} \ 45 | --dataset_type=${DATA_TYPE} 46 | done 47 | done 48 | -------------------------------------------------------------------------------- /scripts/train_cifar100_semi.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | export CUDA_VISIBLE_DEVICES=0 3 | 4 | # supply your working directory here 5 | PATH_ROOT=${PWD}/ 6 | SEMI_SIGN=True 7 | 8 | 9 | for i in 1 10 | do 11 | 12 | TRAIN_DIR=${PATH_ROOT}results/cifar100/semi_split$i 13 | DATASET_DIR_L=${PATH_ROOT}tfrecords/cifar100/train/split$i/labeled/ 14 | DATASET_DIR_U=${PATH_ROOT}tfrecords/cifar100/train/split$i/unlabeled/ 15 | 16 | python train.py \ 17 | --train_dir=${TRAIN_DIR} \ 18 | --dataset_name=cifar100 \ 19 | --dataset_dir_l=${DATASET_DIR_L} \ 20 | --dataset_dir_u=${DATASET_DIR_U} \ 21 | --preprocessing='cifar' \ 22 | --batch_size=100 \ 23 | --num_epochs=500 \ 24 | --num_classes=100 \ 25 | --num_train_l=10000 \ 26 | --num_train_u=40000 \ 27 | --semi=${SEMI_SIGN} \ 28 | --num_gpus=1 29 | 30 | 31 | CHECKPOINT=${PATH_ROOT}results/cifar100/semi_split$i 32 | EVALUATE=${PATH_ROOT}results/ 33 | DATASET_DIR=${PATH_ROOT}tfrecords/cifar100/test/ 34 | SAVE_FILE=cifar100.txt 35 | 36 | python test.py \ 37 | --dataset_name=cifar100 \ 38 | --preprocessing='cifar' \ 39 | --checkpoint_path=${CHECKPOINT} \ 40 | --dataset_dir=${DATASET_DIR} \ 41 | --eval_dir=${EVALUATE} \ 42 | --save_txt=${SAVE_FILE} \ 43 | --batch_size=100 \ 44 | --num_classes=100 45 | 46 | done 47 | -------------------------------------------------------------------------------- /scripts/train_cifar10_semi.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | export CUDA_VISIBLE_DEVICES=0 3 | 4 | # supply your working directory here 5 | PATH_ROOT=${PWD}/ 6 | SEMI_SIGN=True 7 | 8 | 9 | for i in 1 10 | do 11 | 12 | TRAIN_DIR=${PATH_ROOT}results/cifar10/semi_split$i 13 | DATASET_DIR_L=${PATH_ROOT}tfrecords/cifar10/train/split$i/labeled/ 14 | DATASET_DIR_U=${PATH_ROOT}tfrecords/cifar10/train/split$i/unlabeled/ 15 | 16 | python train.py \ 17 | --train_dir=${TRAIN_DIR} \ 18 | --dataset_name=cifar10 \ 19 | --dataset_dir_l=${DATASET_DIR_L} \ 20 | --dataset_dir_u=${DATASET_DIR_U} \ 21 | --preprocessing='cifar' \ 22 | --batch_size=100 \ 23 | --num_epochs=500 \ 24 | --num_classes=10 \ 25 | --num_train_l=4000 \ 26 | --num_train_u=46000 \ 27 | --semi=${SEMI_SIGN} \ 28 | --num_gpus=1 29 | 30 | 31 | CHECKPOINT=${PATH_ROOT}results/cifar10/semi_split$i/ 32 | EVALUATE=${PATH_ROOT}results/ 33 | DATASET_DIR=${PATH_ROOT}tfrecords/cifar10/test/ 34 | SAVE_FILE=cifar10.txt 35 | 36 | python test.py \ 37 | --dataset_name=cifar10 \ 38 | --preprocessing='cifar' \ 39 | --checkpoint_path=${CHECKPOINT} \ 40 | --dataset_dir=${DATASET_DIR} \ 41 | --eval_dir=${EVALUATE} \ 42 | --save_txt=${SAVE_FILE} \ 43 | --batch_size=100 \ 44 | --num_classes=10 45 | 46 | done 47 | -------------------------------------------------------------------------------- /scripts/train_svhn_semi.sh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env bash 2 | export CUDA_VISIBLE_DEVICES=0 3 | 4 | # supply your working directory here 5 | PATH_ROOT=${PWD}/ 6 | SEMI_SIGN=True 7 | 8 | 9 | for i in 1 10 | do 11 | 12 | TRAIN_DIR=${PATH_ROOT}results/svhn/semi_split$i 13 | DATASET_DIR_L=${PATH_ROOT}tfrecords/svhn/train/split$i/labeled/ 14 | DATASET_DIR_U=${PATH_ROOT}tfrecords/svhn/train/split$i/unlabeled/ 15 | 16 | python train.py \ 17 | --train_dir=${TRAIN_DIR} \ 18 | --dataset_name=svhn \ 19 | --dataset_dir_l=${DATASET_DIR_L} \ 20 | --dataset_dir_u=${DATASET_DIR_U} \ 21 | --preprocessing='svhn' \ 22 | --batch_size=100 \ 23 | --num_epochs=200 \ 24 | --num_classes=10 \ 25 | --num_train_l=1000 \ 26 | --num_train_u=72257 \ 27 | --semi=${SEMI_SIGN} \ 28 | --num_gpus=1 29 | 30 | 31 | CHECKPOINT=${PATH_ROOT}results/svhn/semi_split$i/ 32 | EVALUATE=${PATH_ROOT}results/ 33 | DATASET_DIR=${PATH_ROOT}tfrecords/svhn/test/ 34 | SAVE_FILE=svhn.txt 35 | 36 | python test.py \ 37 | --dataset_name=svhn \ 38 | --preprocessing='svhn' \ 39 | --checkpoint_path=${CHECKPOINT} \ 40 | --dataset_dir=${DATASET_DIR} \ 41 | --eval_dir=${EVALUATE} \ 42 | --save_txt=${SAVE_FILE} \ 43 | --batch_size=16 \ 44 | --num_classes=10 \ 45 | --num_examples=26032 46 | 47 | done 48 | -------------------------------------------------------------------------------- /semi_learner.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import os.path 6 | import re 7 | import time 8 | import numpy as np 9 | import tensorflow as tf 10 | import tensorflow.contrib.slim as slim 11 | import network 12 | import memory 13 | import utils 14 | import math 15 | 16 | FLAGS = tf.app.flags.FLAGS 17 | 18 | 19 | def _build_training_graph(images, labels, num_classes, reuse_variables=None): 20 | 21 | with tf.variable_scope(tf.get_variable_scope(), reuse=reuse_variables): 22 | logits, features = \ 23 | network.inference(images, num_classes, for_training=True, feature_name=FLAGS.feature_name) 24 | 25 | labels_l, logits_l, logits_u, features_l, features_u = memory.label_ulabel(labels, logits, features) 26 | 27 | keys, values = memory.module(reuse_variables, labels_l, features_l, tf.nn.softmax(logits_l)) 28 | 29 | mem_pred, net_pred = memory.assimilation(keys, values, labels_l, features_u, logits) 30 | 31 | loss_s = network.loss_ce(logits_l, labels_l) 32 | loss_m = memory.accomodation(mem_pred, net_pred)*FLAGS.weight_u 33 | losses = [loss_s + loss_m] 34 | 35 | regularization_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES) 36 | total_loss = tf.add_n(losses + regularization_losses, name='total_loss') 37 | 38 | loss_averages = tf.train.ExponentialMovingAverage(0.9, name='avg') 39 | loss_averages_op = loss_averages.apply(losses + [total_loss]) 40 | for l in losses + [total_loss]: 41 | loss_name = re.sub('%s_[0-9]*/' % network.TOWER_NAME, '', l.op.name) 42 | tf.summary.scalar(loss_name + ' (raw)', l) 43 | tf.summary.scalar(loss_name, loss_averages.average(l)) 44 | 45 | with tf.control_dependencies([loss_averages_op]): 46 | total_loss = tf.identity(total_loss) 47 | 48 | return total_loss, loss_s, loss_m, labels_l, logits_l, [keys, values] 49 | 50 | 51 | def train(): 52 | with tf.Graph().as_default(), tf.device('/cpu:0'): 53 | assert FLAGS.batch_size % FLAGS.num_gpus == 0, ('Batch size must be divisible by number of GPUs') 54 | 55 | bs_l = FLAGS.batch_size * FLAGS.label_ratio 56 | bs_u = FLAGS.batch_size * (1 - FLAGS.label_ratio) 57 | num_iter_per_epoch = int(FLAGS.num_train_u / bs_u) 58 | max_steps = int(FLAGS.num_epochs * num_iter_per_epoch) 59 | num_classes = FLAGS.num_classes 60 | 61 | global_step = slim.create_global_step() 62 | lr = tf.placeholder(tf.float32, shape=[], name="learning_rate") 63 | opt = tf.train.MomentumOptimizer(learning_rate=lr, momentum=0.9, use_nesterov=True) 64 | 65 | images_l, labels_l = utils.prepare_traindata(FLAGS.dataset_dir_l, int(bs_l)) 66 | images_u, labels_u = utils.prepare_traindata(FLAGS.dataset_dir_u, int(bs_u)) 67 | 68 | images_splits_l = tf.split(images_l, FLAGS.num_gpus, 0) 69 | images_splits_u = tf.split(images_u, FLAGS.num_gpus, 0) 70 | labels_splits_l = tf.split(labels_l, FLAGS.num_gpus, 0) 71 | labels_splits_u = tf.split(labels_u, FLAGS.num_gpus, 0) 72 | 73 | images_splits = [] 74 | labels_splits = [] 75 | for i in range(FLAGS.num_gpus): 76 | images_splits.append(tf.concat([images_splits_l[i], images_splits_u[i]], 0)) 77 | labels_splits.append(tf.concat([labels_splits_l[i], labels_splits_u[i]], 0)) 78 | 79 | tower_grads = [] 80 | top_1_op = [] 81 | memory_op = [] 82 | reuse_variables = None 83 | for i in range(FLAGS.num_gpus): 84 | with tf.device('/gpu:%d' % i): 85 | with tf.name_scope('%s_%d' % (network.TOWER_NAME, i)) as scope: 86 | with slim.arg_scope(slim.get_model_variables(scope=scope), device='/cpu:0'): 87 | loss, loss_s, loss_m, labels, logits, memory_update = \ 88 | _build_training_graph(images_splits[i], labels_splits[i], num_classes, reuse_variables) 89 | 90 | memory_op.append(memory_update) 91 | top_1_op.append(tf.nn.in_top_k(logits, labels, 1)) 92 | 93 | reuse_variables = True 94 | summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, scope) 95 | batchnorm = tf.get_collection(tf.GraphKeys.UPDATE_OPS, scope) 96 | grads = opt.compute_gradients(loss) 97 | tower_grads.append(grads) 98 | 99 | grads = network.average_gradients(tower_grads) 100 | gradient_op = opt.apply_gradients(grads, global_step=global_step) 101 | 102 | var_averages = tf.train.ExponentialMovingAverage(FLAGS.ema_decay, global_step) 103 | var_op = var_averages.apply(tf.trainable_variables()) 104 | 105 | batchnorm_op = tf.group(*batchnorm) 106 | train_op = tf.group(gradient_op, var_op, batchnorm_op) 107 | 108 | saver = tf.train.Saver(tf.global_variables(), max_to_keep=None) 109 | summary_op = tf.summary.merge(summaries) 110 | init_op = tf.global_variables_initializer() 111 | 112 | config = tf.ConfigProto(allow_soft_placement=True, log_device_placement=False) 113 | if FLAGS.gpu_memory: 114 | config.gpu_options.per_process_gpu_memory_fraction = FLAGS.gpu_memory 115 | sess = tf.Session(config=config) 116 | 117 | boundaries, values = utils.config_lr(max_steps) 118 | sess.run([init_op], feed_dict={lr: values[0]}) 119 | 120 | tf.train.start_queue_runners(sess=sess) 121 | summary_writer = tf.summary.FileWriter(FLAGS.train_dir, graph=sess.graph) 122 | 123 | iter_count = epoch = sum_loss = sum_loss_s = sum_loss_m = sum_top_1 = 0 124 | start = time.time() 125 | 126 | for step in range(max_steps): 127 | 128 | decayed_lr = utils.decay_lr(step, boundaries, values, max_steps) 129 | _, _, loss_value, loss_value_s, loss_value_m, top_1_value = \ 130 | sess.run([train_op, memory_op, loss, loss_s, loss_m, top_1_op], feed_dict={lr: decayed_lr}) 131 | 132 | sum_loss += loss_value 133 | sum_loss_s += loss_value_s 134 | sum_loss_m += loss_value_m 135 | top_1_value = np.sum(top_1_value) / bs_l 136 | sum_top_1 += top_1_value 137 | iter_count +=1 138 | 139 | assert not np.isnan(loss_value), 'Model diverged with loss = NaN' 140 | assert not np.isnan(loss_value_s), 'Model diverged with loss = NaN' 141 | assert not np.isnan(loss_value_m), 'Model diverged with loss = NaN' 142 | 143 | if step % num_iter_per_epoch == 0 and step > 0: 144 | end = time.time() 145 | sum_loss = sum_loss / num_iter_per_epoch 146 | sum_loss_s = sum_loss_s / num_iter_per_epoch 147 | sum_loss_m = sum_loss_m / num_iter_per_epoch 148 | sum_top_1 = min(sum_top_1 / num_iter_per_epoch, 1.0) 149 | time_per_iter = float(end - start) / iter_count 150 | format_str = ('epoch %d, L = %.2f, Ls = %.2f, Lm = %.2f, top_1 = %.2f, lr = %.4f (time_per_iter: %.4f s)') 151 | print(format_str % (epoch, sum_loss, sum_loss_s, sum_loss_m, sum_top_1*100, decayed_lr, time_per_iter)) 152 | epoch +=1 153 | sum_loss = sum_loss_s = sum_loss_m = sum_top_1 = 0 154 | 155 | if step % 100 == 0: 156 | summary_str = sess.run(summary_op, feed_dict={lr: decayed_lr}) 157 | summary_writer.add_summary(summary_str, step) 158 | 159 | if (step + 1) == max_steps: 160 | checkpoint_path = os.path.join(FLAGS.train_dir, 'model.ckpt') 161 | saver.save(sess, checkpoint_path, global_step=epoch) 162 | -------------------------------------------------------------------------------- /sup_learner.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import os.path 6 | import re 7 | import time 8 | import numpy as np 9 | import tensorflow as tf 10 | import tensorflow.contrib.slim as slim 11 | import network 12 | import utils 13 | 14 | FLAGS = tf.app.flags.FLAGS 15 | 16 | 17 | def _build_training_graph(images, labels, num_classes, reuse_variables=None): 18 | with tf.variable_scope(tf.get_variable_scope(), reuse=reuse_variables): 19 | logits, features = \ 20 | network.inference(images, num_classes, for_training=True, feature_name=FLAGS.feature_name) 21 | 22 | losses = [network.loss_ce(logits, labels)] 23 | regularization_losses = tf.get_collection(tf.GraphKeys.REGULARIZATION_LOSSES) 24 | total_loss = tf.add_n(losses + regularization_losses, name='total_loss') 25 | 26 | loss_averages = tf.train.ExponentialMovingAverage(0.9, name='avg') 27 | loss_averages_op = loss_averages.apply(losses + [total_loss]) 28 | for l in losses + [total_loss]: 29 | loss_name = re.sub('%s_[0-9]*/' % network.TOWER_NAME, '', l.op.name) 30 | tf.summary.scalar(loss_name + ' (raw)', l) 31 | tf.summary.scalar(loss_name, loss_averages.average(l)) 32 | 33 | with tf.control_dependencies([loss_averages_op]): 34 | total_loss = tf.identity(total_loss) 35 | 36 | return total_loss, logits 37 | 38 | 39 | def train(): 40 | with tf.Graph().as_default(), tf.device('/cpu:0'): 41 | assert FLAGS.batch_size % FLAGS.num_gpus == 0, ('Batch size must be divisible by number of GPUs') 42 | 43 | bs_l = FLAGS.batch_size 44 | num_iter_per_epoch = int(FLAGS.num_train_l / bs_l) 45 | max_steps = int(FLAGS.num_epochs * num_iter_per_epoch) 46 | num_classes = FLAGS.num_classes 47 | 48 | global_step = slim.create_global_step() 49 | lr = tf.placeholder(tf.float32, shape=[], name="learning_rate") 50 | opt = tf.train.MomentumOptimizer(learning_rate=lr, momentum=0.9, use_nesterov=True) 51 | 52 | images, labels = utils.prepare_traindata(FLAGS.dataset_dir_l, int(bs_l)) 53 | images_splits = tf.split(images, FLAGS.num_gpus, 0) 54 | labels_splits = tf.split(labels, FLAGS.num_gpus, 0) 55 | 56 | tower_grads = [] 57 | top_1_op = [] 58 | reuse_variables = None 59 | for i in range(FLAGS.num_gpus): 60 | with tf.device('/gpu:%d' % i): 61 | with tf.name_scope('%s_%d' % (network.TOWER_NAME, i)) as scope: 62 | with slim.arg_scope(slim.get_model_variables(scope=scope), device='/cpu:0'): 63 | loss, logits = \ 64 | _build_training_graph(images_splits[i], labels_splits[i], num_classes, reuse_variables) 65 | top_1_op.append(tf.nn.in_top_k(logits, labels_splits[i], 1)) 66 | 67 | reuse_variables = True 68 | summaries = tf.get_collection(tf.GraphKeys.SUMMARIES, scope) 69 | batchnorm_updates = tf.get_collection(tf.GraphKeys.UPDATE_OPS, scope) 70 | grads = opt.compute_gradients(loss) 71 | tower_grads.append(grads) 72 | 73 | grads = network.average_gradients(tower_grads) 74 | gradient_op = opt.apply_gradients(grads, global_step=global_step) 75 | 76 | var_averages = tf.train.ExponentialMovingAverage(FLAGS.ema_decay, global_step) 77 | var_op = var_averages.apply(tf.trainable_variables() + tf.moving_average_variables()) 78 | 79 | batchnorm_op = tf.group(*batchnorm_updates) 80 | train_op = tf.group(gradient_op, var_op, batchnorm_op) 81 | 82 | saver = tf.train.Saver(tf.global_variables(), max_to_keep=None) 83 | summary_op = tf.summary.merge(summaries) 84 | init_op = tf.global_variables_initializer() 85 | 86 | config = tf.ConfigProto(allow_soft_placement=True, log_device_placement=False) 87 | if FLAGS.gpu_memory: 88 | config.gpu_options.per_process_gpu_memory_fraction = FLAGS.gpu_memory 89 | sess = tf.Session(config=config) 90 | 91 | boundaries, values = utils.config_lr(max_steps) 92 | sess.run([init_op], feed_dict={lr: values[0]}) 93 | 94 | tf.train.start_queue_runners(sess=sess) 95 | summary_writer = tf.summary.FileWriter(FLAGS.train_dir, graph=sess.graph) 96 | 97 | iter_count = epoch = sum_loss = sum_top_1 = 0 98 | start = time.time() 99 | 100 | for step in range(max_steps): 101 | 102 | decayed_lr = utils.decay_lr(step, boundaries, values, max_steps) 103 | _, loss_value, top_1_value = \ 104 | sess.run([train_op, loss, top_1_op], feed_dict={lr: decayed_lr}) 105 | 106 | sum_loss += loss_value 107 | top_1_value = np.sum(top_1_value) / bs_l 108 | sum_top_1 += top_1_value 109 | iter_count +=1 110 | 111 | assert not np.isnan(loss_value), 'Model diverged with loss = NaN' 112 | 113 | if step % num_iter_per_epoch == 0: 114 | end = time.time() 115 | sum_loss = sum_loss / num_iter_per_epoch 116 | sum_top_1 = min(sum_top_1 / num_iter_per_epoch, 1.0) 117 | time_per_iter = float(end - start) / iter_count 118 | format_str = ('epoch %d, L = %.2f, top_1 = %.2f, lr = %.4f (time_per_iter: %.4f s)') 119 | print(format_str % (epoch, sum_loss, sum_top_1*100, decayed_lr, time_per_iter)) 120 | epoch +=1 121 | sum_loss = sum_top_1 = 0 122 | 123 | if step % 100 == 0: 124 | summary_str = sess.run(summary_op, feed_dict={lr: decayed_lr}) 125 | summary_writer.add_summary(summary_str, step) 126 | 127 | if (step + 1) == max_steps: 128 | checkpoint_path = os.path.join(FLAGS.train_dir, 'model.ckpt') 129 | saver.save(sess, checkpoint_path, global_step=epoch) 130 | -------------------------------------------------------------------------------- /test.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import time 6 | import math 7 | import numpy as np 8 | import tensorflow as tf 9 | import tensorflow.contrib.slim as slim 10 | import network 11 | import utils 12 | from datetime import datetime 13 | 14 | tf.app.flags.DEFINE_integer('num_classes', 100, 'The number of classes in training data.') 15 | tf.app.flags.DEFINE_integer('num_examples', 10000, 'The number of samples in total.') 16 | tf.app.flags.DEFINE_integer('batch_size', 100, 'The number of samples in each batch.') 17 | tf.app.flags.DEFINE_string('checkpoint_path', '/tmp/tfmodel/', 'The directory where the model was saved.') 18 | tf.app.flags.DEFINE_string('eval_dir', '/tmp/tfmodel/', 'Directory where the results are saved to.') 19 | tf.app.flags.DEFINE_string('save_txt', None, 'The txt file to save result.') 20 | tf.app.flags.DEFINE_string('dataset_name', 'cifar100', 'The name of the dataset to load.') 21 | tf.app.flags.DEFINE_string('dataset_dir', None, 'The directory where the dataset files are stored.') 22 | tf.app.flags.DEFINE_string('model_name', 'convnet', 'The name of the architecture to evaluate.') 23 | tf.app.flags.DEFINE_float('weight_decay', 0.00004, 'The weight decay on the model weights.') 24 | tf.app.flags.DEFINE_string('preprocessing', 'cifar', 'The name of the preprocessing to use.') 25 | tf.app.flags.DEFINE_float('ema_decay', 0.9999, 'If None, then not used.') 26 | tf.app.flags.DEFINE_integer('image_size', 32, 'Image size.') 27 | tf.app.flags.DEFINE_string('feature_name', 'AvgPool', 'Name of the feature layer.') 28 | 29 | FLAGS = tf.app.flags.FLAGS 30 | 31 | 32 | def main(_): 33 | 34 | with tf.Graph().as_default(): 35 | 36 | images, labels = utils.prepare_testdata(FLAGS.dataset_dir, FLAGS.batch_size) 37 | logits, _ = network.inference(images, FLAGS.num_classes, for_training=False, feature_name=FLAGS.feature_name) 38 | 39 | top_1_op = tf.nn.in_top_k(logits, labels, 1) 40 | top_5_op = tf.nn.in_top_k(logits, labels, 5) 41 | 42 | var_averages = tf.train.ExponentialMovingAverage(FLAGS.ema_decay) 43 | var_to_restore = var_averages.variables_to_restore() 44 | saver = tf.train.Saver(var_to_restore) 45 | ckpt = tf.train.get_checkpoint_state(FLAGS.checkpoint_path) 46 | model_checkpoint_path = ckpt.model_checkpoint_path 47 | 48 | init = tf.global_variables_initializer() 49 | 50 | with tf.Session() as sess: 51 | sess.run(init) 52 | saver.restore(sess, model_checkpoint_path) 53 | global_step = ckpt.model_checkpoint_path.split('/')[-1].split('-')[-1] 54 | print('Successfully loaded model from %s at step=%s.' % 55 | (model_checkpoint_path, global_step)) 56 | 57 | coord = tf.train.Coordinator() 58 | try: 59 | threads = [] 60 | for qr in tf.get_collection(tf.GraphKeys.QUEUE_RUNNERS): 61 | threads.extend(qr.create_threads(sess, coord=coord, daemon=True, start=True)) 62 | 63 | num_iter = int(math.ceil(FLAGS.num_examples / FLAGS.batch_size)) 64 | print('num_iter = ' + str(num_iter)) 65 | 66 | # Counts the number of correct predictions. 67 | count_top_1 = count_top_5 = 0.0 68 | total_sample_count = num_iter * FLAGS.batch_size 69 | step = 0 70 | 71 | print('%s: starting evaluation on (%s).' % (datetime.now(), 'test')) 72 | start_time = time.time() 73 | while step < num_iter and not coord.should_stop(): 74 | top_1, top_5 = sess.run([top_1_op, top_5_op]) 75 | count_top_1 += np.sum(top_1) 76 | count_top_5 += np.sum(top_5) 77 | step += 1 78 | # print progress every 20 batches 79 | if step % 20 == 0: 80 | duration = time.time() - start_time 81 | sec_per_batch = duration / 20.0 82 | examples_per_sec = FLAGS.batch_size / sec_per_batch 83 | print('%s: [%d batches out of %d] (%.1f examples/sec; %.3f sec/batch)' 84 | % (datetime.now(), step, num_iter, examples_per_sec, sec_per_batch)) 85 | start_time = time.time() 86 | 87 | # Compute precision @ 1. (accuracy) and print results 88 | precision_at_1 = count_top_1 / total_sample_count 89 | recall_at_5 = count_top_5 / total_sample_count 90 | print('%s: precision @ 1 = %.4f recall @ 5 = %.4f [%d examples]' % 91 | (datetime.now(), precision_at_1, recall_at_5, total_sample_count)) 92 | 93 | # save results into a txt file 94 | file_path = FLAGS.eval_dir+FLAGS.save_txt 95 | text_file = open(file_path, 'a') 96 | text_file.write(FLAGS.checkpoint_path) 97 | text_file.write('\n') 98 | text_file.write('%s: precision @ 1 = %.4f recall @ 5 = %.4f' % 99 | (datetime.now(), precision_at_1, recall_at_5)) 100 | text_file.write('\n') 101 | text_file.close() 102 | 103 | except Exception as e: # pylint: disable=broad-except 104 | coord.request_stop(e) 105 | 106 | coord.request_stop() 107 | coord.join(threads, stop_grace_period_secs=10) 108 | 109 | 110 | if __name__ == '__main__': 111 | tf.app.run() 112 | -------------------------------------------------------------------------------- /train.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | import semi_learner 7 | import sup_learner 8 | 9 | # training settings 10 | tf.app.flags.DEFINE_boolean('semi', True, 'Train in semi-supervised learning mode.') 11 | tf.app.flags.DEFINE_float('label_ratio', 0.5, 'The ratio of labelled data.') 12 | tf.app.flags.DEFINE_integer('num_epochs', 500, "the number of epochs for training") 13 | tf.app.flags.DEFINE_integer('num_gpus', 1, 'How many GPUs to use.') 14 | tf.app.flags.DEFINE_float('ema_decay', 0.9999, 'If None, then not used.') 15 | tf.app.flags.DEFINE_string('train_dir', '/tmp/tfmodel/', 'Where checkpoints and event logs are written to.') 16 | tf.app.flags.DEFINE_float('gpu_memory', None, 'The memory percentage.') 17 | tf.app.flags.DEFINE_string('decay_lr_type', 'linear', 'Three types: linear, cosine, sine.') 18 | 19 | # dataset 20 | tf.app.flags.DEFINE_integer('num_train_l', 10000, 'The number of labelled samples.') 21 | tf.app.flags.DEFINE_integer('num_train_u', 40000, 'The number of unlabelled samples.') 22 | tf.app.flags.DEFINE_integer('num_classes', 100, 'The number of classes in training data.') 23 | tf.app.flags.DEFINE_string('dataset_name', 'cifar100', 'The name of the dataset.') 24 | tf.app.flags.DEFINE_string('dataset_dir_l', None, 'Where the labelled data is stored.') 25 | tf.app.flags.DEFINE_string('dataset_dir_u', None, 'Where the unlabelled data is stored.') 26 | 27 | # preprocessing 28 | tf.app.flags.DEFINE_integer('batch_size', 100, 'The number of samples in each batch.') 29 | tf.app.flags.DEFINE_integer('image_size', 32, 'Image size.') 30 | tf.app.flags.DEFINE_string('preprocessing', 'cifar', 'The type of the preprocessing to use.') 31 | 32 | # network 33 | tf.app.flags.DEFINE_string('model_name', 'convnet', 'The name of the architecture to train.') 34 | tf.app.flags.DEFINE_float('weight_decay', 0.00004, 'The weight decay on the model weights.') 35 | tf.app.flags.DEFINE_float('smoothing', 0.001, 'The smoothing factor in each class label.') 36 | 37 | # memory module 38 | tf.app.flags.DEFINE_float('eta', 0.5, 'The default memory update rate.') 39 | tf.app.flags.DEFINE_float('weight_u', 1.0, 'The weight on unsupervised loss.') 40 | tf.app.flags.DEFINE_string('feature_name', 'AvgPool', 'Name of the feature layer.') 41 | tf.app.flags.DEFINE_integer('feature_dim', 128, 'Dim of the feature layer.') 42 | 43 | FLAGS = tf.app.flags.FLAGS 44 | 45 | 46 | def main(_): 47 | 48 | if tf.gfile.Exists(FLAGS.train_dir): 49 | tf.gfile.DeleteRecursively(FLAGS.train_dir) 50 | tf.gfile.MakeDirs(FLAGS.train_dir) 51 | 52 | if FLAGS.semi: 53 | semi_learner.train() 54 | else: 55 | sup_learner.train() 56 | 57 | 58 | if __name__ == '__main__': 59 | tf.app.run() 60 | -------------------------------------------------------------------------------- /utils.py: -------------------------------------------------------------------------------- 1 | from __future__ import absolute_import 2 | from __future__ import division 3 | from __future__ import print_function 4 | 5 | import tensorflow as tf 6 | import tensorflow.contrib.slim as slim 7 | import numpy as np 8 | import math 9 | from datasets import dataset_factory 10 | from preprocessing import preprocessing_factory 11 | 12 | FLAGS = tf.app.flags.FLAGS 13 | 14 | 15 | def prepare_traindata(dataset_dir, batch_size): 16 | dataset = dataset_factory.get_dataset(FLAGS.dataset_name, 'train', dataset_dir) 17 | provider = slim.dataset_data_provider.DatasetDataProvider(dataset=dataset, num_readers=4, shuffle=True) 18 | 19 | [image, label] = provider.get(['image', 'label']) 20 | 21 | image_preprocessing_fn = preprocessing_factory.get_preprocessing(FLAGS.preprocessing, is_training=True) 22 | image = image_preprocessing_fn(image, FLAGS.image_size, FLAGS.image_size) 23 | 24 | images, labels = tf.train.shuffle_batch([image, label], batch_size=batch_size, num_threads=4, 25 | capacity=8 * batch_size, min_after_dequeue=4 * batch_size) 26 | return images, labels 27 | 28 | 29 | def prepare_testdata(dataset_dir, batch_size): 30 | dataset = dataset_factory.get_dataset(FLAGS.dataset_name, 'test', dataset_dir) 31 | 32 | provider = slim.dataset_data_provider.DatasetDataProvider(dataset, num_readers=1, shuffle=False) 33 | [image, label] = provider.get(['image', 'label']) 34 | 35 | image_preprocessing_fn = preprocessing_factory.get_preprocessing(FLAGS.preprocessing, is_training=False) 36 | image = image_preprocessing_fn(image, FLAGS.image_size, FLAGS.image_size) 37 | 38 | images, labels = tf.train.batch([image, label], batch_size=batch_size, num_threads=1, 39 | capacity=4 * batch_size, allow_smaller_final_batch=False) 40 | return images, labels 41 | 42 | 43 | def config_lr(max_steps): 44 | if 'cifar' in FLAGS.dataset_name: 45 | # start to decay lr at the 250th epoch 46 | boundaries = [int(250.0 / 500.0 * max_steps)] 47 | values = [0.1] 48 | elif 'svhn' in FLAGS.dataset_name: 49 | # start to decay lr at the beginning: 0th epoch 50 | boundaries = [int(0 * max_steps)] 51 | values = [0.02] 52 | return boundaries, values 53 | 54 | 55 | def linear_decay_lr(step, boundaries, values, max_steps): 56 | # decay learning rate linearly 57 | if 'svhn' in FLAGS.dataset_name: 58 | decayed_lr = (float(max_steps - (step + 1)) / float(max_steps)) * values[0] 59 | else: 60 | if step < boundaries[0]: 61 | decayed_lr = values[0] 62 | else: 63 | ratio = (float(max_steps - (step + 1)) / float(max_steps - boundaries[0])) 64 | decayed_lr = ratio * values[0] 65 | return decayed_lr 66 | 67 | 68 | def cos_decay_lr(step, boundaries, values, max_steps): 69 | # decay learning rate with a cosine function 70 | if 'svhn' in FLAGS.dataset_name: 71 | ratio = 1. - (float(max_steps - (step + 1)) / float(max_steps)) 72 | decayed_lr = np.cos(math.pi/2*ratio)* values[0] 73 | else: 74 | if step < boundaries[0]: 75 | decayed_lr = values[0] 76 | else: 77 | ratio = 1. - (float(max_steps - (step + 1)) / float(max_steps - boundaries[0])) 78 | decayed_lr = np.cos(math.pi/2*ratio) 79 | decayed_lr = decayed_lr * values[0] 80 | return decayed_lr 81 | 82 | 83 | def sin_decay_lr(step, boundaries, values, max_steps): 84 | # decay learning rate with a sine function 85 | if 'svhn' in FLAGS.dataset_name: 86 | ratio = 1.- (float(max_steps - (step + 1)) / float(max_steps)) 87 | decayed_lr = 1 - np.sin(math.pi/2*ratio) 88 | decayed_lr = decayed_lr * values[0] 89 | else: 90 | if step < boundaries[0]: 91 | decayed_lr = values[0] 92 | else: 93 | ratio = 1.- (float(max_steps - (step + 1)) / float(max_steps - boundaries[0])) 94 | decayed_lr = 1 - np.sin(math.pi/2*ratio) 95 | decayed_lr = decayed_lr * values[0] 96 | return decayed_lr 97 | 98 | 99 | def decay_lr(step, boundaries, values, max_steps): 100 | # use cosine or sine learning rate decay schedule may further improve results 101 | if FLAGS.decay_lr_type == 'linear': 102 | decayed_lr = linear_decay_lr(step, boundaries, values, max_steps) 103 | elif FLAGS.decay_lr_type == 'cosine': 104 | decayed_lr = cos_decay_lr(step, boundaries, values, max_steps) 105 | elif FLAGS.decay_lr_type == 'sine': 106 | decayed_lr = sin_decay_lr(step, boundaries, values, max_steps) 107 | else: 108 | raise ValueError('decay_lr_type %s was not recognized.') 109 | return decayed_lr 110 | --------------------------------------------------------------------------------