2023年7月3日发(作者:)
迁移学习(TransferLearning)训练复杂的卷积神经⽹络需要⾮常多的标注数据。所谓迁移学习,就是讲⼀个问题上训练好的模型通过简单的调整使其适⽤于⼀个新的问题。根据论⽂DeCAF:A Deep Convolutional Activation Feature for Generic Visual Recognition中的结论,可以保留训练好的Inception-v3模型中所有卷积层的参数,只是替换最后⼀层全连接层。在最后这⼀层全连接之前的⽹络层称之为瓶颈层(bottleneck)。将新的图像通过训练好的卷积神经⽹络直到瓶颈处的过程可以看成事对图像进⾏特征提取的过程。在训练好的Inception-v3模型中,因为将瓶颈处的输出再通过⼀个单层的全连接层神经⽹络可以很好地区分1000种类别的图像,所以有理由认为瓶颈处输出的节点向量可以被作为任何图像的⼀个更加精简且表达能⼒更强的特征向量。蓝⾊点,卷积层间有联合依赖和适应性,不能破坏输⼊数据⽂件夹包含了5个⼦⽂件夹,每⼀个⼦⽂件的名称为⼀种花的名称,代表不同的类别。平均每⼀种花有734张图⽚,每⼀张图⽚都是RGB⾊彩模式的,⼤⼩也不相同。和之前的样例不同,在这⼀节中给出的程序将直接处理没有整理过的图像数据。import globimport port randomimport numpy as npimport tensorflow as tffrom rm import gfile# 模型和样本路径的设置BOTTLENECK_TENSOR_SIZE = 2048BOTTLENECK_TENSOR_NAME = 'pool_3/_reshape:0'JPEG_DATA_TENSOR_NAME = 'DecodeJpeg/contents:0'MODEL_DIR = '../../datasets/inception_dec_2015'MODEL_FILE= 'tensorflow_inception_'CACHE_DIR = '../../datasets/bottleneck'INPUT_DATA = '../../datasets/flower_photos'VALIDATION_PERCENTAGE = 10TEST_PERCENTAGE = 10# 神经⽹络参数的设置LEARNING_RATE = 0.01STEPS = 4000BATCH = 100# 把样本中所有的图⽚列表并按训练、验证、测试数据分开def create_image_lists(testing_percentage, validation_percentage): result = {} sub_dirs = [x[0] for x in (INPUT_DATA)] is_root_dir = True for sub_dir in sub_dirs: if is_root_dir: is_root_dir = False continue extensions = ['jpg', 'jpeg', 'JPG', 'JPEG'] file_list = [] dir_name = me(sub_dir) for extension in extensions: file_glob = (INPUT_DATA, dir_name, '*.' + extension) file_((file_glob)) if not file_list: continue label_name = dir_()
# 初始化 training_images = [] testing_images = [] validation_images = [] for file_name in file_list: base_name = me(file_name)
# 随机划分数据 chance = t(100) if chance < validation_percentage: validation_(base_name) elif chance < (testing_percentage + validation_percentage): testing_(base_name) else: training_(base_name) result[label_name] = { 'dir': dir_name, 'training': training_images, 'testing': testing_images, 'validation': validation_images, } return result# 定义函数通过cache/input路径、类别名称、图⽚编号和所属数据集获取⼀张图⽚的bottleneck/input地址。def get_image_path(image_lists, image_dir, label_name, index, category): label_lists = image_lists[label_name] category_list = label_lists[category] mod_index = index % len(category_list) base_name = category_list[mod_index] sub_dir = label_lists['dir'] full_path = (image_dir, sub_dir, base_name) return full_path# 定义函数获取Inception-v3模型处理之后的特征向量的⽂件地址。def get_bottleneck_path(image_lists, label_name, index, category): return get_image_path(image_lists, CACHE_DIR, label_name, index, category) + '.txt'# 定义函数使⽤加载的训练好的Inception-v3模型处理⼀张图⽚,得到这个图⽚的特征向量。def process_image_to_bottleneck(sess, image_data, image_data_tensor, bottleneck_tensor): bottleneck_values = (bottleneck_tensor, {image_data_tensor: image_data}) bottleneck_values = e(bottleneck_values) return bottleneck_values# 定义函数先试图寻找已经计算且保存下来的特征向量,如果找不到则先计算这个特征向量,然后保存到⽂件。def get_or_create_bottleneck(sess, image_lists, label_name, index, category, jpeg_data_tensor, bottleneck_tensor): # 得到⼦⽂件夹图像列表 label_lists = image_lists[label_name] sub_dir = label_lists['dir'] sub_dir_path = (CACHE_DIR, sub_dir) # 如果cache⽂件夹中中不存在该⼦⽬录则新建 if not (sub_dir_path): rs(sub_dir_path) # 获取Inception-v3模型处理之后的特征向量的⽂件地址 bottleneck_path = get_bottleneck_path(image_lists, label_name, index, category) # 如果未处理过将原始图⽚数据送⼊模型进⾏处理;并将每个数据⽤逗号分开 if not (bottleneck_path): image_path = get_image_path(image_lists, INPUT_DATA, label_name, index, category) image_data = ile(image_path, 'rb').read() bottleneck_values = process_image_to_bottleneck(sess, image_data, jpeg_data_tensor, bottleneck_tensor) bottleneck_string = ','.join(str(x) for x in bottleneck_values) with open(bottleneck_path, 'w') as bottleneck_file: bottleneck_(bottleneck_string) # 如果处理过直接读取 else: with open(bottleneck_path, 'r') as bottleneck_file: bottleneck_string = bottleneck_() bottleneck_values = [float(x) for x in bottleneck_(',')] return bottleneck_values# 随机获取数据。def get_random_cached_bottlenecks(sess, image_lists, n_classes, how_many, category, jpeg_data_tensor, bottleneck_tensor): """ label is subdir_() train,validate获取随机⼀个batch的图⽚,test获得全部图⽚ """
bottlenecks = [] ground_truths = [] if category == 'testing': label_name_list = list(image_()) for label_index, label_name in enumerate(label_name_list): for image_index, _ in enumerate(image_lists[label_name][category]): makedata() else: for _ in range(how_many): label_index = nge(n_classes) label_name = list(image_())[label_index] image_index = nge(65536) makedata() def makedata(): bottleneck = get_or_create_bottleneck(sess, image_lists, label_name, image_index, category, jpeg_data_tensor, bottleneck_tensor) ground_truth = (n_classes, dtype=32) ground_truth[label_index] = 1.0 (bottleneck) ground_(ground_truth) return bottlenecks, ground_truths# 定义主函数。def main(_): # 整理好的数据 image_lists = create_image_lists(TEST_PERCENTAGE, VALIDATION_PERCENTAGE) # ⼦⽂件夹的数量 n_classes = len(image_())
# 读取已经训练好的Inception-v3模型。 # ile(path,decodestyle) 函数功能:实现对图⽚的读取。 函数参数:(1)path:图⽚所在路径 (2)decodestyle:图⽚的解码⽅式。(‘r’:UTF-8编码; ‘rb’:⾮UTF-8编码) with ile((MODEL_DIR, MODEL_FILE), 'rb') as f: graph_def = ef() graph_romString(()) # 得到的与return_element中的名称相对应的操作和/或张量对象的列表 bottleneck_tensor, jpeg_data_tensor = _graph_def( graph_def, return_elements=[BOTTLENECK_TENSOR_NAME, JPEG_DATA_TENSOR_NAME]) # 定义新的神经⽹络输⼊ bottleneck_input = older(32, [None, BOTTLENECK_TENSOR_SIZE], name='BottleneckInputPlaceholder') ground_truth_input = older(32, [None, n_classes], name='GroundTruthInput')
# 定义⼀层全链接层 with _scope('final_training_ops'): weights = le(ted_normal([BOTTLENECK_TENSOR_SIZE, n_classes], stddev=0.001)) biases = le(([n_classes])) logits = (bottleneck_input, weights) + biases final_tensor = x(logits)
# 定义交叉熵损失函数。 cross_entropy = x_cross_entropy_with_logits(logits=logits, labels=ground_truth_input) cross_entropy_mean = _mean(cross_entropy) train_step = ntDescentOptimizer(LEARNING_RATE).minimize(cross_entropy_mean)
# 计算正确率。 with _scope('evaluation'): correct_prediction = ((final_tensor, 1), (ground_truth_input, 1)) evaluation_step = _mean((correct_prediction, 32)) with n() as sess: init = _variables_initializer() (init) # 训练过程。 for i in range(STEPS):
train_bottlenecks, train_ground_truth = get_random_cached_bottlenecks( sess, image_lists, n_classes, BATCH, 'training', jpeg_data_tensor, bottleneck_tensor) (train_step, feed_dict={bottleneck_input: train_bottlenecks, ground_truth_input: train_ground_truth}) if i % 100 == 0 or i + 1 == STEPS: validation_bottlenecks, validation_ground_truth = get_random_cached_bottlenecks( sess, image_lists, n_classes, BATCH, 'validation', jpeg_data_tensor, bottleneck_tensor) validation_accuracy = (evaluation_step, feed_dict={ bottleneck_input: validation_bottlenecks, ground_truth_input: validation_ground_truth}) print('After %d steps: Validation accuracy on random sampled %d examples = %.1f%%' % (i, BATCH, validation_accuracy * 100))
# 在最后的测试数据上测试正确率。 test_bottlenecks, test_ground_truth = get_random_cached_bottlenecks( sess, image_lists, n_classes, BATCH, 'testing', jpeg_data_tensor, bottleneck_tensor) test_accuracy = (evaluation_step, feed_dict={ bottleneck_input: test_bottlenecks, ground_truth_input: test_ground_truth}) print('Final test accuracy = %.1f%%' % (test_accuracy * 100))if __name__ == '__main__': main(_)### 1. 定义需要使⽤到的常量import globimport port numpy as npimport tensorflow as tffrom rm import gfile# 原始输⼊数据的⽬录,这个⽬录下有5个⼦⽬录,每个⼦⽬录底下保存这属于该# 类别的所有图⽚。INPUT_DATA = '../../datasets/flower_photos'# 输出⽂件地址。我们将整理后的图⽚数据通过numpy的格式保存。OUTPUT_FILE = '../../datasets/flower_processed_'# 测试数据和验证数据⽐例。VALIDATION_PERCENTAGE = 10TEST_PERCENTAGE = 10### 2. 定义数据处理过程# 读取数据并将数据分割成训练数据、验证数据和测试数据。def create_image_lists(sess, testing_percentage, validation_percentage): sub_dirs = [x[0] for x in (INPUT_DATA)] is_root_dir = True
# 初始化各个数据集。 training_images = [] training_labels = [] testing_images = [] testing_labels = [] validation_images = [] validation_labels = [] current_label = 0
# 读取所有的⼦⽬录。 for sub_dir in sub_dirs: if is_root_dir: is_root_dir = False continue # 获取⼀个⼦⽬录中所有的图⽚⽂件。 extensions = ['jpg', 'jpeg', 'JPG', 'JPEG'] file_list = [] dir_name = me(sub_dir) for extension in extensions: file_glob = (INPUT_DATA, dir_name, '*.' + extension) file_((file_glob)) if not file_list: continue print("processing:", dir_name)
i = 0 # 处理图⽚数据。 for file_name in file_list: i += 1 # 读取并解析图⽚,将图⽚转化为299*299以⽅便inception-v3模型来处理。 image_raw_data = ile(file_name, 'rb').read() image = _jpeg(image_raw_data) if != 32: image = t_image_dtype(image, dtype=32) image = _images(image, [299, 299]) image_value = (image)
# 随机划分数据聚。 chance = t(100) if chance < validation_percentage: validation_(image_value) validation_(current_label) elif chance < (testing_percentage + validation_percentage): testing_(image_value) testing_(current_label) else: training_(image_value) training_(current_label) if i % 200 == 0: print(i, "images processed.") current_label += 1
# 将训练数据随机打乱以获得更好的训练效果。 state = _state() e(training_images) _state(state) e(training_labels)
return y([training_images, training_labels, validation_images, validation_labels, testing_images, testing_labels])### 3. 运⾏数据处理过程with n() as sess: processed_data = create_image_lists(sess, TEST_PERCENTAGE, VALIDATION_PERCENTAGE) # 通过numpy格式保存处理后的数据。 (OUTPUT_FILE, processed_data)### 1. 定义训练过程中将要使⽤到的常量。**因为GitHub⽆法保存⼤于100M的⽂件,所以在运⾏时需要先⾃⾏从Google下载inception_⽂件。**import globimport port numpy as npimport tensorflow as tffrom rm import gfileimport as slim# 加载通过TensorFlow-Slim定义好的inception_v3模型。import ion_v3 as inception_v3# 处理好之后的数据⽂件。INPUT_DATA = '../../datasets/flower_processed_'# 保存训练好的模型的路径。TRAIN_FILE = 'train_dir/model'# ⾕歌提供的训练好的模型⽂件地址。因为GitHub⽆法保存⼤于100M的⽂件,所以# 在运⾏时需要先⾃⾏从Google下载inception_⽂件。CKPT_FILE = '../../datasets/inception_'# 定义训练中使⽤的参数。LEARNING_RATE = 0.0001STEPS = 300BATCH = 32N_CLASSES = 5# 不需要从⾕歌训练好的模型中加载的参数。CHECKPOINT_EXCLUDE_SCOPES = 'InceptionV3/Logits,InceptionV3/AuxLogits'# 需要训练的⽹络层参数明层,在fine-tuning的过程中就是最后的全联接层。TRAINABLE_SCOPES='InceptionV3/Logits,InceptionV3/AuxLogit'### 2. 获取所有需要从⾕歌训练好的模型中加载的参数。def get_tuned_variables(): # 不需要从⾕歌训练好的模型中加载的参数。 exclusions = [() for scope in CHECKPOINT_EXCLUDE_(',')] variables_to_restore = [] # 枚举inception-v3模型中所有的参数,然后判断是否需要从加载列表中移除。 for var in _model_variables(): excluded = False for exclusion in exclusions: if with(exclusion): excluded = True break if not excluded: variables_to_(var) return variables_to_restore return variables_to_restore### 3. 获取所有需要训练的变量列表。def get_trainable_variables():
scopes = [() for scope in TRAINABLE_(',')] variables_to_train = []
# 枚举所有需要训练的参数前缀,并通过这些前缀找到所有需要训练的参数。 for scope in scopes: variables = _collection(BLE_VARIABLES, scope) variables_to_(variables) return variables_to_train### 4. 定义训练过程。def main(): # 加载预处理好的数据。 processed_data = (INPUT_DATA) training_images = processed_data[0] n_training_example = len(training_images) training_labels = processed_data[1]
validation_images = processed_data[2] validation_labels = processed_data[3]
testing_images = processed_data[4] testing_labels = processed_data[5] print("%d training examples, %d validation examples and %d testing examples." % ( n_training_example, len(validation_labels), len(testing_labels))) # 定义inception-v3的输⼊,images为输⼊图⽚,labels为每⼀张图⽚对应的标签。 images = older(32, [None, 299, 299, 3], name='input_images') labels = older(64, [None], name='labels')
# 定义inception-v3模型。因为⾕歌给出的只有模型参数取值,所以这⾥ # 需要在这个代码中定义inception-v3的模型结构。虽然理论上需要区分训练和 # 测试中使⽤到的模型,也就是说在测试时应该使⽤is_training=False,但是 # 因为预先训练好的inception-v3模型中使⽤的batch normalization参数与 # 新的数据会有出⼊,所以这⾥直接使⽤同⼀个模型来做测试。 with _scope(inception_ion_v3_arg_scope()): logits, _ = inception_ion_v3( images, num_classes=N_CLASSES, is_training=True)
trainable_variables = get_trainable_variables() # 定义损失函数和训练过程。 x_cross_entropy(_hot(labels, N_CLASSES), logits, weights=1.0) total_loss = _total_loss() train_step = pOptimizer(LEARNING_RATE).minimize(total_loss)
# 计算正确率。 with _scope('evaluation'): correct_prediction = ((logits, 1), labels) evaluation_step = _mean((correct_prediction, 32))
# 定义加载Google训练好的Inception-v3模型的Saver。 load_fn = _from_checkpoint_fn( CKPT_FILE, get_tuned_variables(), ignore_missing_vars=True)
# 定义保存新模型的Saver。 saver = ()
with n() as sess: # 初始化没有加载进来的变量,⼀定要在模型加载之前否则会重新赋值 init = _variables_initializer() (init)
# 加载⾕歌已经训练好的模型。 print('Loading tuned variables from %s' % CKPT_FILE) load_fn(sess)
start = 0 start = 0 end = BATCH for i in range(STEPS):
_, loss = ([train_step, total_loss], feed_dict={ images: training_images[start:end],
labels: training_labels[start:end]}) if i % 30 == 0 or i + 1 == STEPS: (sess, TRAIN_FILE, global_step=i)
validation_accuracy = (evaluation_step, feed_dict={ images: validation_images, labels: validation_labels}) print('Step %d: Training loss is %.1f Validation accuracy = %.1f%%' % ( i, loss, validation_accuracy * 100.0))
start = end if start == n_training_example: start = 0
end = start + BATCH if end > n_training_example:
end = n_training_example
# 在最后的测试数据上测试正确率。 test_accuracy = (evaluation_step, feed_dict={ images: testing_images, labels: testing_labels}) print('Final test accuracy = %.1f%%' % (test_accuracy * 100))### 5. 运⾏训练过程。if __name__ == '__main__': main()迁移学习,是指利⽤数据、任务、或模型之间的相似性,将在旧领域学习过的模型,应⽤于新领域的⼀种学习过程。迁移学习最权威的综述⽂章是⾹港科技⼤学杨强教授团队的A survey on transfer learning[Pan and Yang, 2010]。⼤数据与少标注之间的⽭盾。尽管我们可以获取到海量的数据,这些数据往往是很初级的原始形态,很少有数据被加以正确的⼈⼯标注。数据的标注是⼀个耗时且昂贵的操作,⽬前为⽌,尚未有⾏之有效的⽅式来解决这⼀问题。这给机器学习和深度学习的模型训练和更新带来了挑战。反过来说,特定的领域,因为没有⾜够的标定数据⽤来学习,使得这些领域⼀直不能很好的发展。迁移数据标注,利⽤迁移学习的思想,我们可以寻找⼀些与⽬标数据相近的有标注的数据,从⽽利⽤这些数据来构建模型,增加我们⽬标数据的标注。⼤数据与弱计算之间的⽭盾。绝⼤多数普通⽤户是不可能具有这些强计算能⼒的。这就引发了⼤数据和弱计算之间的⽭盾。模型迁移,利⽤迁移学习的思想,我们可以将那些⼤公司在⼤数据上训练好的模型,迁移到我们的任务中。针对于我们的任务进⾏微调,从⽽我们也可以拥有在⼤数据上训练好的模型。普适化模型与个性化需求之间的⽭盾。⽬前的情况是,我们对于每⼀个通⽤的任务都构建了⼀个通⽤的模型。这个模型可以解决绝⼤多数的公共问题。但是具体到每个个体、每个需求,都存在其唯⼀性和特异性,⼀个普适化的通⽤模型根本⽆法满⾜。⾃适应学习,我们利⽤迁移学习的思想,进⾏⾃适应的学习。考虑到不同⽤户之间的相似性和差异性,我们对普适化模型进⾏灵活的调整,以便完成我们的任务。特定应⽤的需求。⽐如推荐系统的冷启动问题。⼀个新的推荐系统,没有⾜够的⽤户数据,如何进⾏精准的推荐? ⼀个崭新的图⽚标注系统,没有⾜够的标签,如何进⾏精准的服务?现实世界中的应⽤驱动着我们去开发更加便捷更加⾼效的机器学习⽅法来加以解决。为了满⾜特定领域应⽤的需求,我们可以利⽤上述介绍过的⼿段,从数据和模型⽅法上进⾏迁移学习。⽐较项⽬数据分布数据标注模型传统机器学习训练和测试数据服从相同的分布需要⾜够的数据标注来训练模型每个任务分别建模模型迁移学习训练和测试数据服从不同的分布不需要⾜够的数据标注可以在不同任务之间迁移按照⽬标领域有⽆标签,迁移学习可以分为以下三个⼤类:1. 监督迁移学习(Supervised Transfer Learning)2. 半监督迁移学习(Semi-Supervised Transfer Learning)3. ⽆监督迁移学习(Unsupervised Transfer Learning)按学习⽅法的分类形式,最早在迁移学习领域的权威综述⽂章[Pan and Yang, 2010] 给出定义。它将迁移学习⽅法分为以下四个⼤类:1. 基于样本的迁移学习⽅法(Instance based Transfer Learning),简单来说就是通过权重重⽤,对源域和⽬标域的样例进⾏迁移。就是说直接对不同的样本赋予不同权重,⽐如说相似的样本,我就给它⾼权重,这样我就完成了迁移,⾮常简单⾮常⾮常直接。2. 基于特征的迁移学习⽅法(Feature based Transfer Learning),,就是更进⼀步对特征进⾏变换。意思是说,假设源域和⽬标域的特征原来不在⼀个空间,或者说它们在原来那个空间上不相似,那我们就想办法把它们变换到⼀个空间⾥⾯,那这些特征不就相似了?这个思路也⾮常直接。这个⽅法是⽤得⾮常多的,⼀直在研究,⽬前是感觉是研究最热的。3. 基于模型的迁移学习⽅法(Model based Transfer Learning),就是说构建参数共享的模型。这个主要就是在神经⽹络⾥⾯⽤的特别多,因为神经⽹络的结构可以直接进⾏迁移。⽐如说神经⽹络最经典的finetune 就是模型参数迁移的很好的体现。4. 基于关系的迁移学习⽅法(Relation based Transfer Learning),,这个⽅法⽤的⽐较少,这个主要就是说挖掘和利⽤关系进⾏类⽐迁移。⽐如⽼师上课、学⽣听课就可以类⽐为公司开会的场景。按照特征的属性进⾏分类,也是⼀种常⽤的分类⽅法。这在最近的迁移学习综述[Weiss et al., 2016]中给出。按照特征属性,迁移学习可以分为两个⼤类:1. 同构迁移学习(Homogeneous Transfer Learning)2. 异构迁移学习(Heterogeneous Transfer Learning)如果特征语义和维度都相同,那么就是同构;反之,如果特征完全不相同,那么就是异构。举个例⼦来说,不同图⽚的迁移,就可以认为是同构;⽽图⽚到⽂本的迁移,则是异构的。时间序列⾏为识别(Activity Recognition) 主要通过佩戴在⽤户⾝体上的传感器,研究⽤户的⾏为。⾏为数据是⼀种时间序列数据。不同⽤户、不同环境、不同位置、不同设备,都会导致时间序列数据的分布发⽣变化。此时,也需要进⾏迁移学习。图12展⽰了同⼀⽤户不同位置的信号差异性。在这个领域,华盛顿州⽴⼤学的Diane Cook 等⼈在2013 年发表的关于迁移学习在⾏为识别领域的综述⽂章[Cook et al., 2013] 是很好的参考资料。领域(Domain): 是进⾏学习的主体。领域主要由两部分构成:数据和⽣成这些数据的概率分布。通常我们⽤花体D 来表⽰⼀个domain,⽤⼤写斜体P 来表⽰⼀个概率分布。任务(Task): 是学习的⽬标。任务主要由两部分组成:标签和标签对应的函数。通常我们⽤花体Y 来表⽰⼀个标签空间,⽤f(*) 来表⽰⼀个学习函数。找到相似性(不变量),是进⾏迁移学习的核⼼。有了这种相似性后,下⼀步⼯作就是,如何度量和利⽤这种相似性。度量⼯作的⽬标有两点:⼀是很好地度量两个领域的相似性,不仅定性地告诉我们它们是否相似,更定量地给出相似程度。⼆是以度量为准则,通过我们所要采⽤的学习⼿段,增⼤两个领域之间的相似性,从⽽完成迁移学习。深度迁移学习由于深度学习直接对原始数据进⾏学习,所以其对⽐⾮深度⽅法还有两个优势:⾃动化地提取更具表现⼒的特征,以及满⾜了实际应⽤中的端到端(End-to-End) 需求。.深度迁移学习⽅法(BA、DDC、DAN) 对⽐传统迁移学习⽅法(TCA、GFK 等),在精度上具有⽆可匹敌的优势。深度神经⽹络前⾯⼏层都学习到的是通⽤的特征(general feature);随着⽹络层次的加深,后⾯的⽹络更偏重于学习任务特定的特征(specific feature)。这⾮常好理解,我们也都很好接受。那么问题来了:如何得知哪些层能够学习到general feature,哪些层能够学习到specific feature。更进⼀步:如果应⽤于迁移学习,如何决定该迁移哪些层、固定哪些层?⼀个典型的迁移学习过程是这样的。⾸先通过transfer learning对新的数据集进⾏训练,训练过⼀定epoch之后,改⽤fine tune⽅法继续训练,同时降低学习率。这样做是因为如果⼀开始就采⽤fine tune⽅法的话,⽹络还没有适应新的数据,那么在进⾏参数更新的时候,⽐较⼤的梯度可能会导致原本训练的⽐较好的参数被污染,反⽽导致效果下降。借助setup_to_transfer_learning与setup_to_fine_tune这两个函数,我们先只训练模型的顶层,再训练模型的⼤多数层,进⽽在提⾼模型训练效果的同时,降低训练时间。例⼦第⼀种即所谓的transfer learning,迁移训练时,移掉最顶层,⽐如ImageNet训练任务的顶层就是⼀个1000输出的全连接层,换上新的顶层,⽐如输出为10的全连接层,然后训练的时候,只训练最后两层,即原⽹络的倒数第⼆层和新换的全连接输出层。可以说transfer learning将底层的⽹络当做了⼀个特征提取器来使⽤。第⼆种叫做fine tune,和transfer learning⼀样,换⼀个新的顶层,但是这⼀次在训练的过程中,所有的(或⼤部分)其它层都会经过训练。也就是底层的权重也会随着训练进⾏调整。notop:指模型不包含最后的3个全连接层。⽤来做fine-tuning专⽤,专门开源了这类模型。ations. mobilenet. MobileNet ( include_top=True, # 是否保留顶层的3个全连接⽹络 pop函数,去掉最后⼀层。1old_() weights='imagenet', # None代表随机初始化,即不加载预训练权重。'imagenet'代表加载预训练权重 input_tensor=None, # 可填⼊Keras tensor作为模型的图像输出tensor input_shape=None, # 可选,仅当include_top=False有效,应为长为3的tuple,指明输⼊图⽚的shape,图⽚的宽⾼必须⼤于71,如(150,150,3) pooling=None, # 当include_top=False时,该参数指定了池化⽅式。None代表不池化,最后⼀个卷积层的输出为4D张量。‘avg’代表全局平均池化,‘max’代表全局最⼤值池化 classes=1000 # 可选,图⽚分类的类别数,仅当include_top=True并且不加载预训练权重时可⽤。)from net import MobileNet##⽅式(1)base_model = MobileNet(weights='imagenet',include_top=False)##⽅式(2)base_model = MobileNet(weights='G:mobilenet_1_0_128_tf_no_top.h5')x = base_x = GlobalAveragePooling2D()(x)x = Dense(1024,activation='relu')(x) #we add dense layers so that the model can learn more complex functions and classify for better results.x = Dense(1024,activation='relu')(x) #dense layer 2x = Dense(512,activation='relu')(x) #dense layer 3preds = Dense(3,activation='softmax')(x) #final layer with softmax activationmodel = Model(inputs=base_,outputs=preds)y()print(len())for layer in [:20]: ble=Falsefor layer in [20:]: ble=True
from import img_to_array, array_to_imgdef preprocess_input_new(x): img = preprocess_input(img_to_array(x)) return array_to_img(img)# train_datagen = ImageDataGenerator(rescale = 1./255,horizontal_flip = True,fill_mode = "nearest",zoom_range = 0.3,width_shift_range = 0.3,height_shift_range=0.3,rotatiotrain_datagen=ImageDataGenerator(preprocessing_function=preprocess_input_new)
#included in our dependenciestrain_generator=train__from_directory('./train/', # data folder target_size=(224,224), color_mode='rgb', batch_size=32, class_mode='categorical', shuffle=True)test_generator = _from_directory("test",
image_size,
shuffle=False, batch_size=batch_size,
class_mode=None) # 测试集由于没有label,⽣成test_generator的函数需加参数class_mode=None。# 早停法是⼀种被⼴泛使⽤的⽅法,在很多案例上都⽐正则化的⽅法要好。是在训练中计算模型在验证集上的表现,当模型在验证集上的表现开始下降的时候,停⽌训练,这样就# 1. 将原始的训练数据集划分成训练集和验证集# 2. 只在训练集上进⾏训练,并每隔⼀个周期计算模型在验证集上的误差# 3. 当模型在验证集上(权重的更新低于某个阈值;预测的错误率低于某个阈值;达到⼀定的迭代次数),则停⽌训练# 4. 使⽤上⼀次迭代结果中的参数作为模型的最终参数checkpoint = ModelCheckpoint("vgg16_1.h5", monitor='val_acc', verbose=1,
save_best_only=True, save_weights_only=False,
mode='auto',
period=1)early = EarlyStopping(monitor='val_acc',
min_delta=0,
patience=10,
verbose=1,
mode='auto')
e(optimizer='Adam',loss='categorical_crossentropy',metrics=['accuracy'])step_size_train=train_generator.n//train___generator(generator=train_generator, steps_per_epoch=step_size_train, epochs=5, validation_data = validation_generator, callbacks = [checkpoint, early])借助setup_to_transfer_learning与setup_to_fine_tune这两个函数,我们先只训练模型的顶层,再训练模型的⼤多数层,进⽽在提⾼模型训练效果的同时,降低训练时间。def setup_to_transfer_learning(model,base_model): # 这个函数将base_model的所有层都设置为不可训练,顶层默认为可训练。 for layer in base_: ble = False e(optimizer='adam', loss='categorical_crossentropy', metrics=['accuracy'])
def setup_to_fine_tune(model,base_model): # 这个函数将base_model中的前⼏层设置为不可训练,后⾯的所有层都设置为可训练。具体应确定到第⼏层,还需通过模型结构与不断调试来确定。 GAP_LAYER = 17
for layer in base_[:GAP_LAYER+1]: ble = False for layer in base_[GAP_LAYER+1:]: ble = True e(optimizer=Adagrad(lr=0.0001), loss='categorical_crossentropy', metrics=['accuracy'])setup_to_transfer_learning(model,base_model)history_tl = _generator(generator=train_generator, steps_per_epoch=800, epochs=2, validation_data=val_generator, validation_steps=12, class_weight='auto')('./flowers17_iv3_tl.h5')setup_to_fine_tune(model,base_model)history_ft = _generator(generator=train_generator, steps_per_epoch=800, epochs=2, validation_data=val_generator, validation_steps=1, class_weight='auto')('./flowers17_iv3_ft.h5')(2)加载权重到不同的⽹络结构#old modelmodel = Sequential()(Dense(2, input_dim=3, name="dense_1"))(Dense(3, name="dense_2"))_weights(fname)# new modelmodel = Sequential()(Dense(2, input_dim=3, name="dense_1")) # will be (Dense(10, name="new_dense")) # will not be loaded# load weights from first model; will only affect the first layer, dense__weights(fname, by_name=True)layer_dict = dict([(, layer) for layer in ])import h5pyweights_path = 'vgg19_weights.h5'
# ('/fchollet/deep-learning-models/releases/download/v0.1/vgg19_weights_tf_dim_ordering_tf_kernels.h5)f = (weights_path)list(f["model_weights"].keys())layer_names = [ for layer in ]for i in layer_(): weight_names = f["model_weights"][i].attrs["weight_names"] weights = [f["model_weights"][i][j] for j in weight_names] index = layer_(i) [index].set_weights(weights)新数据集较⼩,和原数据集相似,如果我们尝试训练整个⽹络,容易导致过拟合。由于新数据和原数据相似,因此我们期望卷积⽹络中的⾼层特征和新数据集相关。因此,建议冻结所有卷积层,只训练分类器(⽐如,线性分类器):for layer in : ble = False新数据集较⼤,和原数据集相似,由于我们有更多数据,我们更有⾃信,如果尝试对整个⽹络进⾏精细调整,不会导致过拟合。for layer in :
ble = True # 其实默认值就是True新数据集很⼩,但和原数据很不⼀样,由于数据集很⼩,我们⼤概想要从靠前的层提取特征,然后在此之上训练⼀个分类器:(假定你对h5py有所了解)新数据集很⼤,和原数据很不⼀样,由于你有⼀个很⼤的数据集,你可以设计你⾃⼰的⽹络,或者使⽤现有的⽹络。你可以基于随机初始化权重或预训练⽹络权重初始化训练⽹络。⼀般选择后者。keras 样例由imagenet花到猫狗# 基于VGG16迁移学习# 通过Keras的ImageDataGenerator加载数据集# 加载VGG16模型但是不包括输出层input_tensor = (shape=(64, 64, 3))vgg_model = 16(weights='imagenet', include_top=False, input_tensor=input_tensor)layer_dict = dict([(, layer) for layer in vgg_])print(len(layer_dict))vgg_y()num_classes = 3data_generator = ataGenerator( rescale=1./255, shear_range=0.2, zoom_range=0.2, horizontal_flip=True)train_generator = data__from_directory( '/content/drive/My Drive/transferlearndata/train', target_size=(64, 64), batch_size=4, shuffle=True, class_mode='categorical')print(train_s)print(train__indices)data_generator = ataGenerator(rescale=1./255)validation_generator = data__from_directory( '/content/drive/My Drive/transferlearndata/validate', target_size=(64, 64), batch_size=4, class_mode='categorical')# 构建迁移学习⽹络使⽤VGG6的前⾯两个权重block,# 依赖block2_pool的输出,输⼊张量(64x64x3)# 构建⽹络的层x = vgg_x = ormalization()(x)x = n()(x)x = (4096, activation='relu')(x)x = t(0.25)(x)x = (num_classes, activation=x)(x)my_model = (inputs=vgg_, outputs=x)my_y()# 是否fine-tuning整个⽹络或者⼏层for layer in my_[:-10]: ble = False# 编译与训练my_e( loss='categorical_crossentropy', optimizer=(0.0001), metrics=['accuracy'])my__generator(train_generator, epochs=10, validation_data=validation_generator)# 保存整个模型my_("my_transfer_vgg.h5")# 加载与使⽤flower_dict = ['cats', 'dogs', 'horses']new_model = _model("my_transfer_vgg.h5")root_dir = "/content/drive/My Drive/transferlearndata/test"for file in r(root_dir): src = ((root_dir, file)) img = (src, (64, 64)) img = _dims(img, 0) result = new_t(img) index = (result) print(, index, flower_dict[index]) t(src, flower_dict[index],(50, 50), _HERSHEY_PLAIN, 2.0, (0, 0, 255), 2, 8) ("input", src) y(0)yAllWindows()
发布者:admin,转转请注明出处:http://www.yc00.com/xiaochengxu/1688382607a129681.html
评论列表(0条)