迁移学习:数据不足时如何深度学习
网友 dataunion

使用深度学习技术解决问题的过程中,最常见的障碍在于训练模型过程中所需的海量数据。需要如此多的数据,原因在于机器在学习的过程中会在模型中遇到大量参在面对某一领域的具体问题时,通常可能无法得到构建模型所需规模的数据。然而在一个模型训练任务中针对某种类型数据获得的关系也可以轻松地应用于同一领域的不同问题,这就是所谓的迁移学习。

我认为实现人工智能的难度无异于建造火箭。需要有一个强大的引擎,还有大量的燃料。如果空有强大的引擎但缺乏燃料,火箭肯定是无法上天的。如果只有一个单薄的引擎,有再多燃料也无法起飞。如果要造火箭,强大的引擎和大量燃料是必不可少的。以此来类比深度学习的话,深度学习引擎可以看作火箭引擎,而我们为算法提供的海量数据可以看作是燃料。             — Andrew Ng

最近深度学习技术突然开始大肆流行,并在语言翻译、玩策略游戏,以及无人驾驶汽车等涉及到数百万数据量的领域取得了醒目的成果。使用深度学习技术解决问题的过程中,最常见的障碍在于训练模型过程中所需的海量数据。需要如此多的数据,原因在于机器在学习的过程中会在模型中遇到大量参数。

例如这些模型中常见的参数数量范围包括:

image.png

深度学习

神经网络(即深度学习)是一种分层式结构,但又能堆叠在一起(就像乐高积木)。

深度学习技术其实就是一种大规模神经网络,我们可以将这种网络看作一种流程图,数据从一端进入,相互引用/了解后从另一端输出。我们还可以将神经网络拆分成多个部分,从任何一部分中得到自己需要的推理结果。也许无法得到有意义的结果,但依然可以这样做,例如Google DeepDream就是这样做的。

image.png

规模(模型) ∝ 规模(数据) ∝ 复杂度(问题)

在模型的规模和所需数据量的规模之间存在一种有趣的近似于线性的关系。基本推论在于,对于特定的问题(例如类别的数量),模型必须足够大,以便得到数据之间的关系(例如图片中的材质和形状,文本中的语法,以及语音中的音素)。模型中的前序层可以识别所输入内容中不同组成之间的高级别关系(例如边缘和模式),后续层可以识别有助于最终做决策所需的信息,这些信息通常有助于区分不同的结果。因此如果问题的复杂度较高(例如图像分类),所需的参数数量和数据量就会非常大。

image.png

AlexNet在每个环节“看到”的内容

迁移学习来了!

在面对某一领域的具体问题时,通常可能无法得到构建模型所需规模的数据。然而在一个模型训练任务中针对某种类型数据获得的关系也可以轻松地应用于同一领域的不同问题。这种技术也叫做迁移学习(Transfer Learning)。

image.png

Qiang Yang、Sinno Jialin Pan,“A Survey on Transfer Learning”,IEEE Transactions on Knowledge & Data Engineering,vol. 22, no. , pp. 1345–1359, October 2010, doi:10.1109/TKDE.2009.191

迁移学习就像是一个没人愿意保守的最高机密。尽管业内人人皆知,但外界毫不知情。

image.png

谷歌搜索中,机器学习、深度学习,以及迁移学习三个关键字的搜索趋势变化

根据Awesome — Most Cited Deep Learning Papers所公布的深度学习领域最主要的论文统计,超过50%的论文使用了某种形式的迁移学习或预训练。对于资源(数据和计算能力)有限的人,迁移学习技术的重要性与日俱增,然而这一概念尚未得到应有程度的社会影响。最需要这种技术的人甚至至今都不知道这种技术的存在。

如果深度学习是圣杯,数据是守门人,那么迁移学习就是大门钥匙。

借助迁移学习技术,我们可以直接使用预训练过的模型,这种模型已经通过大量容易获得的数据集进行过训练(虽然是针对完全不同的任务进行训练的,但输入的内容完全相同,只不过输出的结果不同)。随后从中找出输出结果可重用的层。我们可以使用这些层的输出结果充当输入,进而训练出一个所需参数的数量更少,规模也更小的网络。这个小规模网络只需要了解特定问题的内部关系,同时已经通过预培训模型学习过数据中蕴含的模式。通过这种方式,即可将经过训练检测猫咪的模型重新用于再现梵高的画作。

image.png

迁移学习技术的另一个重大收益在于可以对模型进行完善的“通用化”。大型模型往往会与数据过度拟合(Overfit),例如建模所用数据量远远超过隐含的现象数量,在处理未曾见过的数据时效果可能不如测试时那么好。由于迁移学习可以让模型看到不同类型的数据,因此可以习得更出色的底层规则。

过度拟合,更像是学习过程中的死记硬背。 — James Faghmous

迁移学习可减小数据量

假设想要终结裙子到底是蓝黑色还是白金色的争议,首先需要收集大量已获证实是蓝黑色和白金色的裙子图片。如果要使用类似上文提到的方式(包含1.4亿个参数!)自行构建一个准确的模型并对其进行训练,至少需要准备120万张图片,这基本上是无法实现的。这时候可以试试迁移学习。

如果使用迁移学习技术,训练所需的参数数量计算方式如下:

参数的数量 = [规模(输入) + 1] * [规模(输出) + 1]= [2048+1]*[1+1]~ 4098 个参数

所需参数数量由1.4*10?个减少至4*10?3;个,降低了五个数量级!只要收集不到100个图片就够了。松了口气!

如果实在没耐心继续阅读,希望立刻知道裙子的颜色,可以直接跳至本文末尾看看如何自行构建一个这样的模型。

迁移学习循序渐进指南 — 使用示例进行情绪分析

在这个示例中共有72篇影评。

62篇不包含明确的情绪,将用于对模型进行预训练

8篇包含明确的情绪,将用于对模型进行训练

2篇包含明确的情绪,将用于对模型进行测试

由于只有8个包含标签的句子(包含明确情绪的句子),因此首先可以预训练模型进行上下文预测。如果只使用这8个句子训练模型,准确度可达50%(这样的准确度和抛硬币差不多)。

我们将使用迁移学习技术解决这个问题,首先使用62个句子训练模型,随后使用第一个模型的部分内容,以此为基础训练出一个情绪分类器。使用随后8个句子进行训练后,用最后2个句子测试得到了100%的精确度。

第1步

image.png

我们将训练一个对词语之间的关系进行建模的网络。将句子中包含的一个词语传递进去,并尝试预测该词语出现在同一个句子中。在下列代码中嵌入的矩阵其大小为vocabulary x embedding_size,其中存储了代表每个词语的向量(这里的大小为“4”)。

graph = tf.Graph()with graph.as_default(): train_inputs = tf.placeholder(tf.int32, shape=[batch_size]) train_labels = tf.placeholder(tf.int32, shape=[batch_size, 1]) valid_dataset = tf.constant(valid_examples, dtype=tf.int32) with tf.device(‘/cpu:0’): embeddings = tf.Variable(tf.random_uniform([vocabulary_size, embedding_size], -1.0, 1.0)) embed = tf.nn.embedding_lookup(embeddings, train_inputs) nce_weights = tf.Variable(tf.truncated_normal([vocabulary_size, embedding_size], stddev=1.0 / math.sqrt(embedding_size))) nce_biases = tf.Variable(tf.zeros([vocabulary_size])) loss = tf.reduce_mean(tf.nn.nce_loss(nce_weights, nce_biases, embed, train_labels, num_sampled, vocabulary_size)) optimizer = tf.train.GradientDescentOptimizer(1.0).minimize(loss) norm = tf.sqrt(tf.reduce_sum(tf.square(embeddings), 1, keep_dims=True)) normalized_embeddings = embeddings / norm valid_embeddings = tf.nn.embedding_lookup(normalized_embeddings, valid_dataset) similarity = tf.matmul(valid_embeddings, normalized_embeddings, transpose_b=True) init = tf.global_variables_initializer()

pretraining_model.py托管于GitHub,可查看源文件

第2步

image.png

我们继续对这个图表进行训练,让相同上下文中出现的词语可以获得类似的向量表征。我们会对这些句子进行预处理,移除所有停用词(Stop word)并实现标记化(Tokenizing)。随后一次传递一个词语,尽量缩短该词语向量与周边词语之间的距离,并扩大与上下文不包含的随机词语之间的距离。

with tf.Session(graph=graph) as session: init.run() average_loss = 0 for step in range(10001): batch_inputs, batch_labels = generate_batch(batch_size, num_skips, skip_window) feed_dict = {train_inputs: batch_inputs, train_labels: batch_labels} _, loss_val, normalized_embeddings_np = session.run([optimizer, loss, normalized_embeddings], feed_dict=feed_dict) average_loss += loss_val final_embeddings = normalized_embeddings.eval()

training_the_pretrained_model.py托管于GitHub,可查看源文件

第3步

随后我们会试着预测句子的情绪。目前已经有10个(8个训练用,2个测试用)句子带有正面和负面的标签。由于上一步得到的模型已经包含从所有词语中习得的向量,并且这些向量的数值属性可以代表词语的上下文,借此可进一步简化情绪的预测。

此时我们并不直接使用句子,而是将句子的向量设置为所含全部词语的平均值(这一任务实际上是通过类似LSTM的技术实现的)。句子向量将作为输入传递到网络中,输出结果为内容为正面或负面的分数。我们用到了一个隐藏的中间层,并通过带有标签的句子对模型进行训练。如你所见,虽然每次只是用了10个样本,但这个模型实现了100%的准确度。

input = tf.placeholder(<span class="hljs-string"><span class="hljs-string">"float"</span></span>, shape=[<span class="hljs-keyword">None</span>, x_size])
y = tf.placeholder(<span class="hljs-string"><span class="hljs-string">"float"</span></span>, shape=[<span class="hljs-keyword">None</span>, y_size])
w_1 = tf.Variable(tf.random_normal((x_size, h_size), stddev=<span class="hljs-number"><span class="hljs-number">0.1</span></span>))
w_2 = tf.Variable(tf.random_normal((h_size, y_size), stddev=<span class="hljs-number"><span class="hljs-number">0.1</span></span>))
h = tf.nn.sigmoid(tf.matmul(X, w_1))yhat = tf.matmul(h, w_2)predict = tf.argmax(yhat, dimension=<span class="hljs-number"><span class="hljs-number">1</span></span>)cost = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(yhat, y))updates = tf.train.GradientDescentOptimizer(<span class="hljs-number"><span class="hljs-number">0.01</span></span>).minimize(cost)sess = tf.InteractiveSession()init = tf.initialize_all_variables()sess.run(init)<span class="hljs-keyword"><span class="hljs-keyword">for</span></span> epoch <span class="hljs-keyword"><span class="hljs-keyword">in</span></span> range(<span class="hljs-number"><span class="hljs-number">1000</span></span>): <span class="hljs-keyword"><span class="hljs-keyword">for</span></span> i <span class="hljs-keyword"><span class="hljs-keyword">in</span></span> range(len(train_X)): sess.run(updates, feed_dict={<span class="hljs-string">X:</span> train_X[<span class="hljs-string">i:</span> i + <span class="hljs-number"><span class="hljs-number">1</span></span>], <span class="hljs-string">y:</span> train_y[<span class="hljs-string">i:</span> i + <span class="hljs-number"><span class="hljs-number">1</span></span>]}) train_accuracy = numpy.mean(numpy.argmax(train_y, axis=<span class="hljs-number"><span class="hljs-number">1</span></span>) == sess.run(predict, feed_dict={<span class="hljs-string">X:</span> train_X, <span class="hljs-string">y:</span> train_y})) test_accuracy = numpy.mean(numpy.argmax(test_y, axis=<span class="hljs-number"><span class="hljs-number">1</span></span>) == sess.run(predict, feed_dict={<span class="hljs-string">X:</span> test_X, <span class="hljs-string">y:</span> test_y})) print(<span class="hljs-string"><span class="hljs-string">"Epoch = %d, train accuracy=%.2f%%, test accuracy=%.2f%%"</span></span> % (epoch+<span class="hljs-number"><span class="hljs-number">1</span></span>,<span class="hljs-number"><span class="hljs-number">100.</span></span>*train_accuracy,<span class="hljs-number"><span class="hljs-number">100.</span></span>* test_accuracy))

training_the_sentiment_model.py托管于GitHub,查看源文件

虽然这只是个示例,但可以发现在迁移学习技术的帮助下,精确度从50%飞速提升至100%。若要查看完整范例和代码请访问下列地址:

https://gist.github.com/prats226/9fffe8ba08e378e3d027610921c51a78

CIO之家 www.ciozj.com 公众号:imciow
关联的文档
也许您喜欢