fast.ai 深度学习笔记(二)(2)https://developer.aliyun.com/article/1482682
批次大小和 BPTT [01:47:40]
在语言模型中发生的情况是,尽管我们有很多电影评论,它们都被连接在一起成为一个大文本块。因此,我们预测这个巨大的长文本中的下一个单词,其中包含所有 IMDB 电影评论的连接。
- 我们将连接的评论分成批次。在这种情况下,我们将其分成 64 个部分。
- 然后我们将每个部分移动到前一个部分的下方,并对其进行转置。
- 我们最终得到一个大小为 100 万乘以 64 的矩阵。
- 然后我们每次抓取一小块,这些块的长度大致等于 BPTT。在这里,我们抓取一个大约 70 个字符长的部分,这是我们放入 GPU(即批次)的第一件事。
next(iter(md.trn_dl)) ''' (Variable containing: 12 567 3 ... 2118 4 2399 35 7 33 ... 6 148 55 227 103 533 ... 4892 31 10 ... ⋱ ... 19 8879 33 ... 41 24 733 552 8250 57 ... 219 57 1777 5 19 2 ... 3099 8 48 [torch.cuda.LongTensor of size 75x64 (GPU 0)], Variable containing: 35 7 33 ⋮ 22 3885 21587 [torch.cuda.LongTensor of size 4800 (GPU 0)]) '''
- 我们通过将数据加载器包装在
iter
中,然后调用next
来获取我们的第一个训练批次。 - 我们得到了一个 75 乘以 64 的张量(大约 70 行,但不完全)
- Torchtext 做的一个巧妙的技巧是每次随机更改
bptt
数字,因此每个时期它都会获取略有不同的文本片段 - 类似于在计算机视觉中对图像进行洗牌。我们不能随机洗牌单词,因为它们需要按正确的顺序排列,所以我们随机移动它们的断点一点点。 - 目标值也是 75 乘以 64,但出于一些技术原因,它被展平为一个单一向量。
问题:为什么不按句子分割?[01:53:40] 不完全是。请记住,我们使用的是列。因此,我们的每一列长度约为 100 万,因此尽管这些列并不总是完全以句号结束,但它们非常长,我们不在乎。每列包含多个句子。
关于这个问题,Jeremy 发现了在这个语言模型矩阵中的内容有一段时间让人有点费解,所以如果需要一段时间并且需要问一千个问题,不要担心。
创建一个模型 [01:55:46]
现在我们有一个可以提供批次的模型数据对象,我们可以创建一个模型。首先,我们将创建一个嵌入矩阵。
这里是:#批次;词汇表中的唯一标记数;数据集的长度;单词数
len(md.trn_dl), md.nt, len(md.trn_ds), len(md.trn_ds[0].text) ''' (4602, 34945, 1, 20621966) '''
这是我们的嵌入矩阵的样子:
- 这是一个高基数分类变量,而且,这是唯一的变量 - 这在自然语言处理中很典型
- 嵌入大小为 200,比我们以前的嵌入向量要大得多。这并不奇怪,因为一个词比“星期天”的概念要复杂得多。一般来说,一个词的嵌入大小会在 50 到 600 之间。
em_sz = 200 # size of each embedding vector nh = 500 # number of hidden activations per layer nl = 3 # number of layers
研究人员发现大量的动量(我们稍后会了解)与这些循环神经网络模型不太兼容,因此我们创建了一个Adam优化器的版本,其动量小于默认值0.9
。每当你在做自然语言处理时,你应该包括这一行:
opt_fn = partial(optim.Adam, betas=(0.7, 0.99))
Fast.ai 使用了由 Stephen Merity 开发的最先进的AWD LSTM 语言模型的变体。这个模型的一个关键特征是通过Dropout提供了出色的正则化。目前还没有简单的方法来找到下面的 dropout 参数的最佳值 - 您只需要进行实验…
然而,其他参数(alpha
,beta
和clip
)通常不需要调整。
learner = md.get_model( opt_fn, em_sz, nh, nl, dropouti=0.05, dropout=0.05, wdrop=0.1, dropoute=0.02, dropouth=0.05 ) learner.reg_fn = partial(seq2seq_reg, alpha=2, beta=1) learner.clip=0.3
- 在最后一堂课中,我们将学习架构是什么以及所有这些 dropout 是什么。现在,只需知道它与通常情况下相同,如果您尝试构建一个 NLP 模型并且欠拟合,那么减少所有这些 dropout,如果过拟合,那么以大致这个比例增加所有这些 dropout。由于这是一篇最近的论文,所以没有太多的指导,但这些比例效果很好 - 这也是 Stephen 一直在使用的。
- 还有另一种我们可以避免过拟合的方法,我们将在最后一堂课上讨论。目前,
learner.reg_fn = partial(seq2seq_reg, alpha=2, beta=1)
可以可靠地工作,因此您所有的 NLP 模型可能都需要这一行。 learner.clip=0.3
:当您查看梯度并将其乘以学习率以决定更新权重的量时,这将不允许它们超过 0.3。这是一个很酷的小技巧,可以防止我们迈出太大的一步。- 细节现在并不太重要,所以您可以按原样使用它们。
问题:有一些词嵌入,如 Word2vec 或 GloVe。它们与这个有什么不同?为什么不最初使用它们来初始化权重?人们以前已经对这些嵌入矩阵进行了预训练,以执行各种其他任务。它们不被称为预训练模型;它们只是一个预训练的嵌入矩阵,您可以下载它们。我们完全可以下载它们。我发现以这种方式构建一个完整的预训练模型似乎并没有从使用预训练词向量中受益,而使用一个完整的预训练语言模型则产生了更大的差异。也许我们可以将两者结合起来使它们变得更好。
问题:模型的架构是什么?我们将在最后一课中学习有关模型架构的知识,但现在,它是使用一种称为 LSTM(长短期记忆)的递归神经网络。
拟合
learner.fit(3e-3, 4, wds=1e-6, cycle_len=1, cycle_mult=2) learner.save_encoder('adam1_enc') learner.fit( 3e-3, 4, wds=1e-6, cycle_len=10, cycle_save_name='adam3_10' ) learner.save_encoder('adam3_10_enc') learner.fit( 3e-3, 1, wds=1e-6, cycle_len=20, cycle_save_name='adam3_20' ) learner.load_cycle('adam3_20',0)
在情感分析部分,我们只需要语言模型的一半 - 编码器,所以我们保存了那部分。
learner.save_encoder('adam3_20_enc') learner.load_encoder('adam3_20_enc')
语言建模的准确性通常使用指标困惑度来衡量,这只是我们使用的损失函数的exp()
。
math.exp(4.165) ''' 64.3926824434624 ''' pickle.dump(TEXT, open(f'{PATH}models/TEXT.pkl','wb'))
测试
我们可以稍微玩弄一下我们的语言模型,以确保它运行正常。首先,让我们创建一小段文本来“引导”一组预测。我们将使用我们的 torchtext 字段对其进行数值化,以便将其馈送给我们的语言模型。
m=learner.model ss=""". So, it wasn't quite was I was expecting, but I really liked it anyway! The best""" s = [spacy_tok(ss)] t=TEXT.numericalize(s) ' '.join(s[0]) ''' ". So , it was n't quite was I was expecting , but I really liked it anyway ! The best" '''
我们还没有添加使测试语言模型变得容易的方法,因此我们需要手动执行这些步骤。
# Set batch size to 1 m[0].bs=1 # Turn off dropout m.eval() # Reset hidden state m.reset() # Get predictions from model res,*_ = m(t) # Put the batch size back to what it was m[0].bs=bs
让我们看看在我们短文本之后的下一个单词的前 10 个预测是什么:
nexts = torch.topk(res[-1], 10)[1] [TEXT.vocab.itos[o] for o in to_np(nexts)] ''' ['film', 'movie', 'of', 'thing', 'part', '<unk>', 'performance', 'scene', ',', 'actor'] '''
…让我们看看我们的模型是否可以自己生成更多文本!
print(ss,"\n") for i in range(50): n=res[-1].topk(2)[1] n = n[1] if n.data[0]==0 else n[0] print(TEXT.vocab.itos[n.data[0]], end=' ') res,*_ = m(n[0].unsqueeze(0)) print('...') ''' . So, it wasn't quite was I was expecting, but I really liked it anyway! The best* *film ever ! <eos> i saw this movie at the toronto international film festival . i was very impressed . i was very impressed with the acting . i was very impressed with the acting . i was surprised to see that the actors were not in the movie . ... '''
情感
所以我们之前已经预训练了一个语言模型,现在我们想要微调它以进行情感分类。
要使用预训练模型,我们将需要语言模型的保存的词汇表,因为我们需要确保相同的单词映射到相同的 ID。
TEXT = pickle.load(open(f'{PATH}models/TEXT.pkl','rb'))
sequential=False
告诉 torchtext 文本字段应该被标记化(在这种情况下,我们只想存储“正面”或“负面”单个标签)。
IMDB_LABEL = data.Field(sequential=False)
这一次,我们需要将每个评论视为单独的而不是作为一个大段的文本,因为每个评论都有不同的情感附着。
splits
是 torchtext 的一个方法,用于创建训练、测试和验证集。IMDB 数据集内置在 torchtext 中,因此我们可以利用它。查看lang_model-arxiv.ipynb
,了解如何定义自己的 fastai/torchtext 数据集。
splits = torchtext.datasets.IMDB.splits(TEXT, IMDB_LABEL, 'data/') t = splits[0].examples[0] t.label, ' '.join(t.text[:16]) ''' ('pos', 'ashanti is a very 70s sort of film ( 1979 , to be precise ) .') '''
fastai 可以直接从 torchtext 的splits
创建一个ModelData
对象。
md2 = TextData.from_splits(PATH, splits, bs)
现在你可以继续调用get_model
来获取我们的学习者。然后我们可以加载预训练的语言模型(load_encoder
)。
m3 = md2.get_model( opt_fn, 1500, bptt, emb_sz=em_sz, n_hid=nh, n_layers=nl, dropout=0.1, dropouti=0.4, wdrop=0.5, dropoute=0.05, dropouth=0.3 ) m3.reg_fn = partial(seq2seq_reg, alpha=2, beta=1) m3.load_encoder(f'adam3_20_enc')
因为我们正在微调一个预训练模型,我们将使用不同的学习率,并增加用于剪切的最大梯度,以使 SGDR 更好地工作。
m3.clip=25. lrs=np.array([1e-4,1e-3,1e-2]) m3.freeze_to(-1) m3.fit(lrs/2, 1, metrics=[accuracy]) m3.unfreeze() m3.fit(lrs, 1, metrics=[accuracy], cycle_len=1) ''' [ 0\. 0.45074 0.28424 0.88458] [ 0\. 0.29202 0.19023 0.92768] '''
我们确保除了最后一层外,所有层都被冻结。然后我们进行一些训练,解冻它,再进行一些训练。好处是一旦你有了一个预训练的语言模型,它实际上训练速度非常快。
m3.fit( lrs, 7, metrics=[accuracy], cycle_len=2, cycle_save_name='imdb2' ) ''' [ 0\. 0.29053 0.18292 0.93241] [ 1\. 0.24058 0.18233 0.93313] [ 2\. 0.24244 0.17261 0.93714] [ 3\. 0.21166 0.17143 0.93866] [ 4\. 0.2062 0.17143 0.94042] [ 5\. 0.18951 0.16591 0.94083] [ 6\. 0.20527 0.16631 0.9393 ] [ 7\. 0.17372 0.16162 0.94159] [ 8\. 0.17434 0.17213 0.94063] [ 9\. 0.16285 0.16073 0.94311] [ 10\. 0.16327 0.17851 0.93998] [ 11\. 0.15795 0.16042 0.94267] [ 12\. 0.1602 0.16015 0.94199] [ 13\. 0.15503 0.1624 0.94171] ''' m3.load_cycle('imdb2', 4) accuracy(*m3.predict_with_targs()) ''' 0.94310897435897434 '''
Bradbury 等人最近发表的一篇论文,学习中的翻译:上下文化的词向量,对解决 IMDB 情感分析问题的最新学术研究进行了方便的总结。许多最新的算法都是针对这个特定问题进行调整的。
正如你所看到的,我们在情感分析方面取得了最新的技术成果,将错误率从 5.9%降低到 5.5%!你应该能够使用相同的基本步骤在其他 NLP 分类问题上获得同样世界级的结果。
有许多机会进一步改进这一点,尽管我们在本课程的第二部分之前不会能够做到这一点。
- 例如,我们可以开始训练查看许多医学期刊的语言模型,然后我们可以制作一个可下载的医学语言模型,然后任何人都可以用它来在医学文献的前列腺癌子集上进行微调。
- 我们还可以将其与预训练的词向量结合使用
- 我们本可以预先训练一个维基百科语料库语言模型,然后将其微调为一个 IMDB 语言模型,然后再将其微调为一个 IMDB 情感分析模型,我们会得到比这个更好的东西。
有一个名为 Sebastian Ruder 的非常出色的研究人员,他是唯一一个真正大量撰写关于 NLP 中预训练、微调和迁移学习的研究人员。Jeremy 问他为什么这种情况没有更多发生,他的观点是因为没有软件使其变得容易。希望 Fast.ai 会改变这一点。
协同过滤介绍 [02:11:38]
数据可从files.grouplens.org/datasets/movielens/ml-latest-small.zip
获取
path='data/ml-latest-small/' ratings = pd.read_csv(path+'ratings.csv') ratings.head()
数据集看起来像这样:
它包含用户的评分。我们的目标是对于我们以前没有见过的某个用户-电影组合,我们必须预测一个评分。
movies = pd.read_csv(path+'movies.csv') movies.head()
为了使其更有趣,我们还将实际下载一份电影列表,以便我们可以解释这些嵌入矩阵中实际包含的内容。
g=ratings.groupby('userId')['rating'].count() topUsers=g.sort_values(ascending=False)[:15] g=ratings.groupby('movieId')['rating'].count() topMovies=g.sort_values(ascending=False)[:15] top_r = ratings.join( topUsers, rsuffix='_r', how='inner', on='userId' ) top_r = top_r.join( topMovies, rsuffix='_r', how='inner', on='movieId' ) pd.crosstab( top_r.userId, top_r.movieId, top_r.rating, aggfunc=np.sum )
这就是我们正在创建的——用户和电影的这种交叉表。
随意提前查看,你会发现大部分步骤对你来说已经很熟悉了。
深度学习 2:第 1 部分第 5 课
原文:
medium.com/@hiromi_suenaga/deep-learning-2-part-1-lesson-5-dd904506bee8
译者:飞龙
来自 fast.ai 课程的个人笔记。随着我继续复习课程以“真正”理解它,这些笔记将继续更新和改进。非常感谢 Jeremy 和Rachel 给了我这个学习的机会。
Lesson 5
I. 介绍
在结构化深度学习方面的出版物还不够,但在行业中肯定正在发生:
您可以使用这个工具从 Google 下载图片并解决自己的问题:
关于如何训练神经网络的介绍(一篇很棒的技术文章):
我们如何‘训练’神经网络? by Vitaly Bushaev
学生们正在与 Jeremy 一起参加Kaggle 幼苗分类竞赛。
II. 协同过滤 — 使用 MovieLens 数据集
讨论的笔记本可以在这里(lesson5-movielens.ipynb)找到。
让我们看看数据。我们将使用userId
(分类)、movieId
(分类)和rating
(依赖)进行建模。
ratings = pd.read_csv(path+'ratings.csv') ratings.head()
为 Excel 创建子集
我们创建了最受欢迎的电影和最痴迷于电影的用户的交叉表,我们将把它复制到 Excel 中进行可视化。
g=ratings.groupby('userId')['rating'].count() topUsers=g.sort_values(ascending=False)[:15] g=ratings.groupby('movieId')['rating'].count() topMovies=g.sort_values(ascending=False)[:15] top_r = ratings.join(topUsers, rsuffix='_r', how='inner', on='userId') top_r = top_r.join(topMovies, rsuffix='_r', how='inner', on='movieId') pd.crosstab(top_r.userId, top_r.movieId, top_r.rating, aggfunc=np.sum)
这是包含上述信息的 Excel 文件。首先,我们将使用矩阵分解而不是构建神经网络。
- 蓝色单元格 — 实际评分
- 紫色单元格 — 我们的预测
- 红色单元格 — 我们的损失函数即均方根误差(RMSE)
- 绿色单元格 — 电影嵌入(随机初始化)
- 橙色单元格 — 用户嵌入(随机初始化)
每个预测是电影嵌入向量和用户嵌入向量的点积。在线性代数术语中,这相当于矩阵乘积,因为一个是行,一个是列。如果没有实际评分,我们将预测设为零(将其视为测试数据 — 而不是训练数据)。
然后我们使用梯度下降来最小化我们的损失。Microsoft Excel 中有一个“求解器”插件,可以通过更改选定的单元格来最小化一个变量(GRG Nonlinear
是您要使用的方法)。
这可以称为“浅层学习”(与深度学习相对),因为没有非线性层或第二个线性层。那么我们刚刚直观地做了什么?每部电影的五个数字称为“嵌入”(潜在因素) - 第一个数字可能表示它有多少科幻和奇幻,第二个可能表示为电影使用了多少特效,第三个可能表示它有多少对话驱动,等等。同样,每个用户也有 5 个数字,例如,表示用户有多喜欢科幻奇幻、特效和对话驱动的电影。我们的预测是这些向量的叉积。由于我们没有每个用户对每部电影的评论,我们试图找出哪些电影与这部电影相似,以及其他用户如何评价这部电影(因此称为“协同”)。
对于新用户或新电影,我们该怎么办 - 我们需要重新训练模型吗?我们现在没有时间来讨论这个问题,但基本上您需要有一个新的用户模型或新的电影模型,最初会使用它,随着时间的推移,您将需要重新训练模型。
简单的 Python 版本[26:03]
这应该现在看起来很熟悉。我们通过选择随机 ID 集创建一个验证集。wd
是 L2 正则化的权重衰减,n_factors
是我们想要的嵌入矩阵有多大。
val_idxs = get_cv_idxs(len(ratings)) wd = 2e-4 n_factors = 50
我们从 CSV 文件创建一个模型数据对象:
cf = CollabFilterDataset.from_csv(path, 'ratings.csv', 'userId', 'movieId', 'rating')
然后我们得到一个适合模型数据的学习器,并拟合模型:
learn = cf.get_learner(n_factors, val_idxs, 64, opt_fn=optim.Adam) learn.fit(1e-2, 2, wds=wd, cycle_len=1, cycle_mult=2)
输出 MSE
由于输出是均方误差,您可以通过以下方式获得 RMSE:
math.sqrt(0.765)
输出约为 0.88,优于 0.91 的基准。
您可以以通常的方式获得预测:
preds = learn.predict()
您还可以使用 seaborn sns
进行绘图(建立在 matplotlib 之上):
y = learn.data.val_y sns.jointplot(preds, y, kind='hex', stat_func=None)
fast.ai 深度学习笔记(二)(4)https://developer.aliyun.com/article/1482686