看Andrew Ng视频,总结的学习心得。
虽然本篇文章可能不是那么细致入微,甚至可能有了解误差。
可是,我喜欢用更直白的方式去理解知识。
上一篇文章传送门: https://segmentfault.com/a/11...面试
我最开始接触的是ML (但只限于Sklearn的简单应用,工程化的内容当时一点都不了解。)
后来有幸了解到DL (这个了解比较多)
我面的是普通Python岗, 由于个人小项目中涉及到 (聊天机器人)。因此第二个面试官揪着这个聊了聊。
与面试官交谈时,我也直接挑明了,模型是Github找的,当时本身爬了些问答对,处理后放入模型本身训练的。
面试官一顿(特征提取,语义)等各类 ML-NLP工程化的过程,把我直接问懵了。。segmentfault
怎么提取特征(问号脸,难道是TF-IDF,分词之类的??)?????
我也不知道说啥,以仅有的能力,和他聊聊(LSTM、Embedding, Seq2Seq的 Encoder-Vector-Decoder)。。网络
面试官说:“你说了这些, 那你特征工程是怎么作的???”
我感受已经没有任何反驳的能力了。。。接下来的事情,我不说,你们也应该清楚了。ide
我回来后也反思过, 作了什么特征工程??
我看视频中 也是,数据简单预处理下,而后分词,词频过滤,构建词典
而后,直接就是构建NN层(包括Embedding层)。函数
直到最后了解了"端到端这个概念" 与 传统ML的区别。
才清楚, 当时面试的场景是怎么个状况。。。源码分析
传统ML: 原数据 -> 数据特征工程(各类复杂的人工处理) ---> 模型
端到端DL:原数据 -----------------------------------------------------> 模型 学习
端到端:(一步到位):优化
传统的ML作的中间层人工"手动"特征工程处理出来的特征。 这些特征,端到端的NN均可能"自动学习"的到。 这也多是当时为何面试官一直追问我"特征如何处理"的缘由吧。也肯能他另有目的QAQ... 或者咱们真的不在一个频道上。。。可是交流的过程真的使我受益不浅,有了更广阔的视野(3Q!)
强调一点:编码
虽然端到端 模型很便捷。可是须要大量的数据,才能训练出好的效果。
卷积层(激活函数) + 池化层 + 全链接层 Convolution + Pooling + Dense
有人喜欢把: 卷积层 + 池化层 做为一层网络 (由于池化层无训练训练,后面会提到) 也有人喜欢把: 卷积层 和 池化层 各自单独算一个层(也是没问题的。Tensorflow的API就是这样设计的)
卷积计算过程就不说了。没有案例图。 但你能够理解为: 两个 正方体 的 对应位置的元素 (相乘再相加)的结果。。。 (互相关,,,)
输出图像大小计算公式:spa
h图片输出 = (h图片输入 - h卷积核 + 2padding) / strides + 1 w图片输出 = (w图片输入 - w卷积核 + 2padding) / strides + 1 首先声明: 这个式子因为不必定可以整除, 所以除不尽的状况下,向下取整, 也叫地板除 由于有个原则: 卷积核滑动的时候(一般是,步长>1的状况下) 若是越界了一部分。则舍弃掉
根据上面的公式,求一个输出图像大小的例子(此处,不作paddding, 而且步长为1)
eg: 输入8x8 的图像 , 并使用3x3的卷积核 输出图像高度为: h图片输出 = (8-3 + 2x0) / 1 + 1 = 6 输出图像宽度为: w图片输出 = (8-3 + 2x0) / 1 + 1 = 6 因此输出图像为: 6x6 很明显: 卷积一次,图像变小了。 若是再卷积几回, 图像就看不到了。。。 因此: 咱们须要解决这个问题 原则上: 增长 padding 能解决步长为1时,卷积后的图片缩放问题。
假如咱们但愿输出图像的大小 等于 输出图像的大小,而咱们想要求padding须要设置为多少。
通常这种场景适用于 步长strides = 1, 因此参考开始的公式,可写出以下公式:
由于: w 和 h是同样的公式,此处只写出一个h,来推导: h图片输出 = (h图片输入 - h卷积核 + 2padding) / strides + 1 化简: padding =( h图片输出 - h图片输入 + h卷积核 - 1 ) / 2 由于咱们但愿的条件: h图片输出 等于 h图片输入, 因此可继续化简: padding =( h卷积核 - 1 ) / 2
因此步长为1的状况下卷积, 而且想让图片不变形,你的padding的取值,就须要受到 卷积核大小的影响。
如今经常使用的卷积核大多都是 1x一、 3x三、 5x3 。因此看上面化简好的公式:
padding =( h卷积核 - 1 ) / 2 <============= 1x1, 3x3, 5x5 奇数-1总等于偶数。 因此不用担忧除不尽的状况 还须要注意一下: 填充padding通常是环形填充, 假如padding=1, 那么上下左右 都会添加一层。 固然: tensorflow的padding是能够设置形状的
valid:
不填充
same:
自动去填充,使得输入图像 与 输出图像 的大小相等
三通道卷积:
假如你只有一个卷积核 即便你图片是3通道的(三层) 即便你卷积核也是三通道的(三层) 可是: 卷积输出结果是依然是一个 m x n 的形状 (一层"薄纸") 你的疑惑: 不是三层嘛? 最后怎么变成一层了 ??? 个人解释: 每滑动一次,3层通道,各自都会计算各自层的卷积,而后求总和,并填入一层"薄纸"的对应位置
多个卷积核:
上面说了: 1个卷积核,不管图片是什么形状的、有几个通道,卷积后输出的形状 是 一层薄纸: m x n 而若是你有多个卷积核: 那么你就会有多层薄纸摞起来。 就像 一个长方形 摞成了一个 长方体 明确点: 假如你用了 6个卷积核, 那么你的输出就变成了 m x n x 6 (三维的一个长方体了)
上面说的:就是我最开始学的CNN时候常常理解不清楚的地方。我相信你也同样, qaq ....
下面作个总结:
C个通道: 一点都不会影响输出维度。 注意我说的是维度。 假如你的输入是 m x n , 那么你的输出依然是 p x q (注意维度便可,维度没变,二维) f个卷积核: 会影响输出的维度。 输出的维度将会增长一个维度f 假如你的输入是 m x n , 那么你的输出依然是 p x q x f (增长一个维度F,变成了) 也许你当你学TF的时候会有另外一个理解障碍:那就是TF数据格式(以图片为例): 一般TF数据格式是这样的: [图片数量B, 图片高度H, 图片宽度W, 通道数C] 假如你使用 F 个卷积核作了卷积: 那么他的卷积结果的特征的形状就变变成: [B, H, W, F] 发现没输出结果和通道数C,没有关系的。 只和 卷积核的个数 f有关系。 可是注意: 虽然结果和C不要紧。可是 须要卷积核中具备 C的数量,还作惟独匹配。桥梁运算。 对应上例, 咱们的卷积核的形状应该是这样的 : [F, C, H, W] 注意一下: 这里面有 卷积核数量f, 也有通道数量C。
若是最后一步的卷积核形状不理解:
不要紧。之后是TF20的天下。 对应API不须要你指定卷积核的形状。 所以,你不必记住卷积核的形状。 你只须要 传递,卷积核的个数, 和 宽高 和 步长 便可。 固然这些都是独立的命名参数。 摘一小段Conv2D的源码: def __init__(self, filters, # 你只须要传递这个参数, 卷积核的个数 kernel_size, # 卷积核的宽高,假如 3x3 你只需写 [3,3] 便可 strides=(1, 1), # 这是步长, 你不传,他也会给你填默认值, 步长为1 padding='valid', # 这时 padding策略,前面说过,这个通常都会设为 "same" 或许你还有些疑问: 刚才上面不是提到了卷积核应该设置 通道数C么。 原则上是的。由于要和 输出的样本作卷积。要匹配才行。 可是在Tensorflow中。 特别是 Tenosrflow.Keras中,定义模型层 咱们只须要把整个模型,从上到下链接起来。(就像前后排队同样) 而对于一些先后流动贯通的参数,好比刚才提到的通道C。 这些参数,Tensorflow会自动帮咱们上下文识别管理。 因此咱们作的只是须要,把原始数据的形状设置好传 给第一层(给排头发数据) 至于你这些在中间层流动的参数,Tensorflow会自动帮你计算,你不用传。 虽然不用传,但你最好清楚每层是什么结构(固然这时后话,可能须要一些时间理解) 到最后,我再给你设置一个输出形状,你能给我输出出来便可 (队尾接数据) 基本TF参数流动机制讲到这里,刚开始学的时候,也是各类苦思冥想,想不明白qaq...
其实咱们作的每一步 (每个)卷积就至关于一个矩阵线性操做: x1 @ w1
以后,基于常理话, 我会还会给它一个误差: b 变成 ===> x1 @ w1 + b
咱们说过,可能会给出不少个卷积核进行运算。
上面 x1 @ w1 + b 是每个卷积核的卷积结果 咱们还须要讲全部卷积核计算结果堆叠在一块儿: 记为 X @ W + b # m x n x f 最后将堆叠在一块儿的结果,作一层非线性变换 relu ( X @ W + b ) # CNN 一般用 relu eg: 现有图片 5 x 5 x 3 的图像 (暂时不考虑样本个数,就认为是一个样本). 咱们用的是 2 x 2 x 20 的卷积核 (步长为1,不作padding) 那么输出结果就是 (5-2+1) x (5-2+1) x 20 === 4 x 4 x 20
忘记说了,还有一个公式,用来计算 每层卷积的权重参数量的个数的:
公式: 每层权重参数量(W) = 卷积核个数 x 卷积核高 x 卷积核宽 x 卷积核通道数 公式: 每层误差数量(b) = 1 x 卷积核的个数 # 由于每一个卷积核只有一个误差b 舒适提示: 有太多人喜欢把卷积核个数 与 卷积核通道称做:"输入/输出"通道。 这样的称呼是没问题的, 但我在计算参数量的时候,不喜欢这样的称呼,易混淆。 前情回顾: 记不记得普通神经网络了。每一个神经元节点,都有它们本身的参数。所以它们的参数量是巨大的 回归正文: 而卷积核是共享的, 由于它是在一张图片上滑动的。(挨个服务)因此权重参数也是共享的。
卷积层(激活函数) => 池化层
池化层主要分两种: MaxPooling 和 AvgPooling
声明: 池化层也有滑动窗口,而且输出形状计算公式,和 卷积的输出形状计算公式同样:
h图片输出 = (h图片输入 - h卷积核 + 2padding) / strides + 1 w图片输出 = (w图片输入 - w卷积核 + 2padding) / strides + 1
由于池化层,的基本都是放在卷积层以后,所以池化层的通道数 也就瓜熟蒂落的 和 卷积层通道同样
举个例子: 卷积层数据形状为: m x n x f 那么池化层形状同为: p x q x f 我想主要强调的是: 通道数不变,变得是 宽高。
仍是,把Tensorflow, 源码搬过来,标注一下:
def __init__(self, pool_size=(2, 2), # 滑动窗口大小 2x2 strides=None, # 步长,一般设为2 padding='valid', # Maxpooling 一般不用padding
通常都是使用组合 pool_size=(2, 2) 和 stride = 2
因此,公式来了: 输入h 滑动窗口h 输出h = (输入h - 滑动窗口h) / stride + 1 = ---------- - -------- + 1 stride stride
一般咱们把 pooling层做称做数据的降采样:
因此大多数经验者,都会把 滑动窗口 和 stride步长 设为相等大小。 因此带入上面公式: 输入h 1 输入h 输出h = (输入h - 滑动窗口h) / stride + 1 = ---------- - ----- + 1 = ------- stride 1 步长 简化一下: (当 pool_size 和 strides 设置相等大小时): 输出 = 输入 / 步长 因此当咱们: 步长设为2时, 输出就是输出的一半。 步长设为3时, 输出就是输出的1/3。 ...
不知道有没有这样一个疑问:”为何滑动窗口没有设置 窗口数量 (就像设置卷积核数量)“
再次说一下Tensorflow的原理。 由于Pooling的上一层基本彻底是 Conv卷积层, 卷积层的 卷积核的个数已经设置好了。 卷积层对接池化层的时候, Tensorflow会自动判断,并设置: 池化层滑动窗口的个数===卷积核个数 池化层通道个数的个数===卷积层通道个数===图片的原始通道个数
卷积操做:以前咱们卷积不是拿着滑动窗口,对应元素相乘再相加么?
池化操做:池化层也是拿着滑动窗口同样滑,可是不作运算,而是只取每一个窗口内最大值。放在一层"薄纸"上
同样滑动窗口,各类滑, 而后取每一个窗口内的数据的"平均值", 其余就不说了,同 MaxPooling
池化层的是"没有"参数能够训练的。因此,反向传播,也不为所动~~~
你很熟悉的, 全链接层其实就是以前讲的普通的NN(神经网络),因此并无什么好说的。
只是拼接在池化层以后罢了。
但其实仍是有一些细节须要注意。尤为以前的东西没搞懂,那么这里的参数形状你会垮掉~~~
以前为了图方便,参数我都没怎么提到样本参数。
下面我要把样本参数也加进来一块儿唠唠了。我感受讲这里,直接上例子比较直观。
好了,如今咱们有个需求, 想要作一个10分类的任务:
卷积层-池化层: 这个照常作, 设置你还能够堆叠 卷积层1 + 池化层1 + 卷积层2 + 池化层2 ... 等堆叠的差很少了: (你自我感受良好了。。。),咱们须要作一层展平处理!
展平处理(特地拿出来讲)
假如你叠加到最后一层池化层数据形状是:(1000,4,4,8)==> 1000个样本,宽高8 x 8, 输出通道为8 你展平后的形状为: (1000, 4*4*8) == (1000, 128) 展平操做第一种API: tf.keras.Flatten() # tensorflow2.0的Flatten被做为网络层使用 展平操做第一种API: tf.reshape(x, [-1, 128]) # 手动变换, -1是补位计算的意思 而后在加全链接层,形状为: (1000, 50) # 50表明输出,起到过渡做用 而后在加全链接层,形状为: (1000, 10) # 最终,10表明输出,由于咱们说了,要作10分类嘛 1. 其实你中间能够加不少全链接层,我这里只加了一层,控制最后一层全链接是你想要的输出就行。 2. 特别注意, 这里的每一层全链接计算,是须要有激活函数跟着的。 除了最后一层全链接,其余层的全链接都设置为 Relu 激活函数便可。 3. 由于咱们作的是10分类(多分类天然应想到 softmax参数, 若是是其余业务,你也能够不加激活函数) 没作,也就是最后一层。咱们要再添加一层激活函数 Softmax。
降采样(控制输出通道数量):
假如,前一个卷积层参数为: (1000,32,32,256) 若是你下一层使用1x1x128的卷积,则对应参数为: (1000,32,32,128) # 256通道变成了128通道
一般CNN大多数都是用来作CV工做。对于某些文本分类。CNN也能够完成。以下变通概念:
是否网络层数越多越好,虽然堆叠更多的网络,可使得参数丰富,而且能够学到更多更好的特征。
可是实际效果并不是如此,而是出现,过拟合等现象。
ResNet做者 何凯明:有感而发:按理说模型是应该越丰富越好的。但是出现了过拟合这种现象。
最少,更深层的网络的效果,应该比浅层网络的效果好吧。不至于差了那么多。
所以,他将此问题转换为一个深度模型优化的问题。
能够看到,上图中有一些输入和输出:慢慢捋清。
而后将上述途中最后2行的公式化简,可获得以下形式:
若是你看过了上面的图,你会很清楚, 有多少个x, 就会输出多少个y。
上面第7点说过 : "其实每层的输出y1 会替代下一层的x做为下一层的输入", 该如何理解这句话???
假如你有这样一段文本: "我精通各类语言" => 分词后的结果会变成 "我","精通","各类","语言" 通常的问答对这种的句子,处理流程是: (这里只先说一个): 那就是:在句子的末尾添加一个 <END> 标识符 因此句子变成了: "我","精通","各类","语言", "END" 这些单词都会预先转为 (One-Hot编码 或者Embedding编码) x1(初始值0) => y1(我) y1有必定几率输出 "我", 下面全部的y同理,只是几率性。 x2(y1) => y2(精通) 如此每一层嵌套下来,至关于条件几率 P(精通|我) x3(y2) => y3(各类) P(各类|(我,精通)) x4(y3) => y4(语言) ... x5(y4) => y5(<END>) ...
不知道看了上例,你会不会有下面一连串的问号脸??:
不是说x1-x5每一个x应该输入固定句子的每一个单词么???为何变成了输入y1-y5
答:的确是这样的,可是咱们的 y1-y5 都是朝着预测x1-x5的方向前进的。(这也是咱们要训练的目标)
因此: 能够近似把y1-y5等价于x1-x5。 因此用 y1-y5 替代了 x1-x5 这样: 也能够把先后单词串联起来,让整个模型具备很强的关联性。 好比: 你第一个y1就预测错了。那么以后的y极可能都预测错。(个人例子是:双色球几率) 可是: 假如咱们预测对了。那说明咱们的模型的效果,已经特别好了(双色球每一个球都预测对了~)
既然你说y 是从词典选拔出来的词的几率属性。 那么这个几率怎么算?
答: 这问得太好了~~~
前面说了: 通常都会预先给数据作 One-Hot或 Embedding编码。 因此数据格式为: [0,0,....,1,...] # 只有一个为1 基本上咱们最后给输出都会套一层: softmax激活函数, softmax应该知道吧:e^x /(e^x1+..+e^x) 因此: softmax结果就是一个 和One-Hot形状同样的几率列表集合: [.....,最高几率,...] softmax的结果(几率列表) :(表明着预测 在词典中每个单词的可能性)
那么损失函数怎么算呢??
答:没错,损失函数也是咱们最关注的。
前面:咱们已经求出了softmax对应的结果列表 (....,最高几率,...) 损失函数: 咱们使用的是交叉熵。 交叉熵知道吧: -( Σp*logq ) # p为真实值One-hot, q为预测值 简单举个例子: 假如 softmax预测结果列表为 :[0.01,0.35, 0.09, 0.55] # 舒适提示,softmax和为1 你的真实标签One-Hot列表为: [0, 0, 0, 1] 那么交叉熵损失就等于: -( 0*log0.01 + 0*log0.35 + 0*log0.09 + 1*log0.55 ) = ... 到此为止,咱们第一层NN的输出的损失函数就已经计算完毕了。 而咱们训练整个网络须要计算总体的损失函数。 因此,咱们须要把上面的交叉熵损失求和, 优化损失。
RNN 的梯度是累乘,因此NN层若是不少,可能会达到 指数级的梯度。
你应该听过一个小关于指数的小案例吧~~ (学如逆水行舟,不进则退~) >>> 1.01 ** 365 37.78343433288728 # 天天进步0.01 ,一年能够进步这些 (对应梯度爆炸) >>> 0.99 ** 365 0.025517964452291125 # 天天退步0.01 , 一年能够沦落至此(对应梯度消失)
梯度爆炸:
就是上面例子的原理。 就很少说了。 解决方式:梯度裁剪
梯度消失:
同上例, 很差解决(因而LSTM网络出现, 和LSTM)
import tensorflow.keras as tk # 注意我用的是TF20标准版,因此这样导入 tk.layers.SimpleRNN( units=单元层, # units单元数,对应着你每一个单词的个数 return_sequences=False # 默认值就是False )
GRU比RNN的每一层的多了一个 记忆信息(至关于RNN的 h),这个记忆信息就像传送带同样,一直流通各层RNN
而后还多了2个门 (r门和U门), 这2个门就是负责控制(是否从传送带上取记忆, 且取多少记忆)
简化版(只有U门):
C新' = tanh( w @ [C旧, x新] + b ) # 根据传动带的旧信息, 生产出 传送带的新信息 u门 = sigmoid (w @ [c旧, x新] + b) # 一个门控单元,起到过滤信息的做用 C新 = u门 * C新' + (1-u门) * C旧 # 通过u门控单元的控制过滤后, 最终放到传送带的信息 若是: u门为1,则传送带上全是新信息(旧的全忘掉) 若是: u门为0,则传送带上全是旧信息(新的不要) 强调一下: 我不方便写公式负号,因而用了 "新","旧" 代替 新: 表明当前 t 旧: 表明前一时刻 t-1
完整版(同时具备r门和u门)
添加这一行:
r门 = sigmoid (w @ [c旧, x新] + b) # 和下面的U门几乎类似,只不过换了一下权重和误差 C新' = tanh( w @ [r门 @ C旧, x新] + b ) # 修改这一行: C旧 ==变为===> r门 @ C旧 u门 = sigmoid (w @ [c旧, x新] + b) # 一个门控单元,起到过滤信息的做用 C新 = u门 * C新' + (1-u门) * C旧 # 通过u门控单元的控制过滤后, 最终放到传送带的信息
import tensorflow.keras as tk # 注意我用的是TF20标准版,因此这样导入 tk.layers.GRU( # 参数同上面RNN我就不解释了 units=64, return_sequences=False # 这些参数看下面LSTM我会讲到 )
LSTM和GRU很像,可是比GRU复杂。
LSTM结构包括: u门(更新门)+ f门(遗忘门)+ o门(输出门)
f门 = sigmoid (w @ [c旧, x新] + b) # 和下面的U门几乎类似,只不过换了一下权重和误差 o门 = sigmoid (w @ [c旧, x新] + b) # 和下面的U门几乎类似,只不过换了一下权重和误差 C新' = tanh( w @ [C旧, x新] + b ) # 注意,这里没有r门了 u门 = sigmoid (w @ [c旧, x新] + b) # 一个门控单元,起到过滤信息的做用 C新 = u门 * C新' + f门 * C旧 # "(1-u门)" 换成了 f门 h = o门 * tanh( C新 )
import tensorflow.keras as tk # 注意我用的是TF20标准版,因此这样导入 keras.layers.LSTM( units=64, return_state=True # 占坑,下面剖析 return_sequences=False # 占坑,下面源码剖析 recurrent_initializer='glorot_uniform', # 均匀分布的权重参数初始化 # stateful=True, # API文档:若为True,则每一批样本的state的状态,都会继续赋予下一批样本 )
return_state 和 return_sequences 这两个参数到底有什么用???
个人另外一篇文章单独源码分析这两个参数:https://segmentfault.com/a/11...
GRU有2个门: u门 和 r门 LSTM有3个门: u门 和 f门 和 o门 GRU有一个C: # 就有一条传送带c, 他的先后单元信息仅靠这一条传送带来沟通(舍弃与保留) LSTM有一个C和一个h: # 不只有传送带c, 还有h, 他的先后单元信息 靠 c 和 h 联合沟通。 再说一下每一个门控单元: 无论你是什么门, 都是由 Sigmoid() 包裹着的。 因此: 说是 0 和 1 , 但严格意义上,只是无穷接近。可是微乎其微,因此咱们理解为近似相等0和1
首先说明:
双向模型,对于 RNN/LSTM/GRU 所有都适用
因为单向的模型,不能关联以后信息。好比:你只能根据以前的单词预测下一个单词。
而双向的模型,能够根据先后上下文的语境,来预测当前的下一个单词。
或者举一个更直白的例子(我本身认为):
好比说: 你作英语完型填空题, 你是否是 须要 把空缺部分的 前面 和 后面 都得读一遍,才能填上。
单向与双向结构对好比下:
单向: 1 -> 2 -> 3 -> 4 双向: 1 -> 2 -> 3 -> 4 | 1 <- 2 <- 3 <- 4 注意: 上下对齐,表明一层。
import tensorflow.keras as tk # 注意我用的是TF20标准版,因此这样导入 tk.layers.Bidirectional( # 就在上上面那些API的基础上,外面嵌套一个 这个层便可。 tk.layers.GRU( units=64, return_sequences=False ) ),
首先说明:
层叠模型对于 RNN/LSTM/GRU 一样所有都适用
以前单层单向模型是这种结构
1 -> 2 -> 3 计算公式是: 单元 = tanh ( W @ (x, h左) )
而多层单向是这种结构(咱们以2层为例):
y1 y2 y3 输出层 ^ ^ ^ | | | 7 -> 8 -> 9 二层单元 ^ ^ ^ | | | 4 -> 5 -> 6 一层单元 ^ ^ ^ | | | x1-> x2 ->x3 输入层 你 好 啊 计算公式是: (我写的可能只按本身的意思了~) 一层每一个单元 = tanh ( W @ (x, h左) ) # 由于是第一层嘛:因此输入为 x 和 左边单元h 二层每一个单元 = tanh ( W @ (h下, h左) ) # 第二层就没有x了:而是下边单元h 和 左边单元h
c1 @ c2 余弦定理,求 cosθ = ------------------ ||c1|| * ||c2|| 或者你可使用欧氏距离。
E @ O 矩阵乘积后记为词向量 W
可见以下案例:
若是: 咱们分词后词典总大小为1000 那么: 他的 One-Hot 矩阵形状为 [6, 1000] (假如咱们这里经过句子6个词来预测最后一个词) 而且: 随机高维权重矩阵 形状为 [1000, 300] (注意,这个300是维度,可自行调整选择) 注意: 上面权重矩阵是随机初始化的, 后面训练调节的。 最后: E @ O 后获得词向量W 的形状为 [6, 300]
说下我的的理解,可能不对
skip-grams: 拿出中间 一个词,来预测若干(这是词距,本身给定)上下文的单词。
例子以下:
seq = 今天去吃饭 给定单词 标签值(y_true) 去 今 去 天 去 吃 去 饭
训练过程就是上面说过的小节 "原始词嵌入并训练",你只需把 y_true改成 "今","天","吃","饭"训练便可。
Word2Vec 除了 skip-grams, 还有 CBOW 模型。 它的做用是 给定上下文,来预测中间的词。
听说效率等某种缘由(softmax计算慢,由于分母巨大),这两个都没看。(Pass~)
解决Word2Vec 的 softmax计算慢。
负采样说明(假如咱们有1000长度的词典):
从上下文(指定词距): 随机,选择一个正样本对,n个负样本对(5-10个便可) 主要机制: 将 Word2Vec的 softmax(1000分类) 换成 1000个 sigmoid作二分类。 由于: 是随机采样(假设,采样1个正样本 和 5个负样本)。 因此: 1000个 sigmoid二分类器,每次只用到 6 个对应分类器(1个正样本分类器,5个负样本分类器)
负采样,样本随机选择公式:
单个词频 ^ (3/4) ----------------- Σ(全部词频 ^ (3/4))