超参数调整

training/dev/test集

dev集就是validation集,用于评估超参数选择的好坏。test集只在训练完成后做一次评估。要确保dev/test集的数据来自同一分布。如果来自实际应用的数据太少,优先划分到dev/test集。

bias和variance

high bias对应欠拟合,high variance对应过拟合

  • 若training集误差比人类误差(比如0)高得多,说明high bias、欠拟合。
  • 若dev集误差比training集误差高得多,说明high variance、过拟合。
  • 若test集误差比dev集误差高得多,说明dev集过拟合,需要扩充dev集。
training集的数据可来自不同分布

如果training集的数据来自不同分布,就要从training集随机取些数据构成training-dev集。training-dev集与training集来自同一分布,但不参与训练,部分取代原先dev集的功能。

  • 若training-dev集误差比training集误差高得多,说明high variance、过拟合。
  • 若training-dev集误差和training集误差差不多,但dev集误差比training-dev集误差高得多,说明数据不匹配。这时要多制造或者采集符合dev/test集特征的training集数据。
防止过拟合

regularization(正则化):在代价函数J(w,b)中给权重矩阵w添加惩罚。一般用L2正则 \(||w||_2^2 = \sum w_j^2\),效果是参数值减小。 > L1正则 \(|w|_1 = \sum |w_j|\) 效果是参数值为0。因为|w|图像关于y轴对称,在x=0处取极小值。

dropout(随机失活):每次前向-后向传播中,各层随机保留keep-prob比例的单元,并将单元输出/=keep-prob以弥补关掉单元的输出。各层的keep-prob参数可以不同。

data augmentation(数据扩展):比如把图片翻转、旋转、切变、裁切、变色等。

输入归一化(normalize)

\(x = (x-μ)/σ\),均值归0、方差归1

梯度爆炸或消失

梯度爆炸只需clipping掉太大的梯度。

梯度消失可从一些方面缓解,比如激活函数、权重矩阵初始化、跨层连接

  • 将激活函数从tanh/sigmoid换成relu,因为relu当x>0时梯度为1,乘上不会减小。
  • 将权重矩阵初始化为单位矩阵I。更常用Xavier初始化,使输入输出方差相同:\(rand()\sqrt{ \frac{1}{n_{in}} }\)\(n_{in}\)是该层输入单元的个数;若激活函数是relu,有一半情况不激活:\(rand()\sqrt{ \frac{2}{n_{in}} }\)
  • ResNet中跨层连接;RNN中使用GRU或LSTM等门单元。
mini-batch

每次只取m个样本,在这mini-batch上梯度下降。m大时算得快,因为mini-batch整体向量化参与计算。经验上最好m<=32,小mini-batch训练出的模型泛化能力更强。 特例:m=1,叫作stochastic梯度下降;m=总样本数,叫作batch梯度下降。

梯度下降的加速

高维空间容易遇到“鞍点”减慢下降速度,下面算法都能解决鞍点问题。

常用的Adam算法整合了Momentum和RMSprop

Momentum

指数加权移动平均:\(v_t = βv_{t-1} + (1-β)*θ_t\)。可修正初始偏差 \(v_t / (1-β^t)\)

用梯度的移动平均更新权重: \(V_{dW} = βV_{dW} + (1-β)dW\)\(W=W-αV_{dW}\)。b同理更新。

RMSprop(root mean square prop)

\(S_{dW}=βS_{dW} + (1-β)dW^2(dW^2按元素乘\)),\(W=W-α\frac{dW}{\sqrt{S_{dW}}}\)。 因为\(dW\)值较小,\(S_{dW}\)较小,\(\frac{dW}{\sqrt{S_{dW}}}\)较大,W移动变快,W方向收敛加快。

b同理更新,b移动变慢,b方向振荡减小。

学习率衰减(learning rate decay)

跑一遍数据叫1 epoch,随着epoch增加,减小学习率。

提前停止

一般不用这方法,因为它同时有两种不正交的影响:提高了dev集拟合、降低train集拟合。

Batch Norm

隐藏层在应用激活函数a=g(z)前,先对该层z值进行归一化,使它们有相同的均值(不一定是0)和方差(不一定是1)。

新添BN层,输入z值计算均值μ和方差σ,\(z_{norm}^{(i)} = \frac{z^{(i)} - μ}{\sqrt{σ^2 + ε}}\)\(\tilde{z}^{(i)} = γz_{norm}^{(i)} + β\)。其中γ和β是待学习的参数,输出值\(\tilde{z}^{(i)}\)代替\(z^{(i)}\)进激活函数层。

使用BN时 \(z^{[l]} = W^{[l]}a^{[l-1]} + b^{[l]}\) 中的\(b^{[l]}\)项可省略,不省略也将被归一化减掉。

训练时在一个个mini-batch上应用BN,更新μ和σ的移动平均;测试时用μ和σ的移动平均作总体μ和σ的估算。

迁移学习

为利用大数据量任务A训练好的深度网络N,相同类型的小数据量任务B换掉N的最后几层换上新层,然后在最后一些层上更新权重(其余层不更新权重)。

如果更新所有层的权重,那么原先A上的训练叫做pre-training,现在B上的训练叫做fine-tuning。


CNN

物体分类和定位一起做

图片中单物体定位:输出标签 anchorBox [ 图片是否包含物体?,若包含时物体定位框的四个点,物体的各个分类]。

滑动窗口算法:一小块一小块窗口卷积地看图片是否包含物体。 全卷积网络优化:把Convs-FullyConnected的网络改成Convs-1x1Convs的全卷积网络,然后在整个图片上卷积,一下子算出了各块窗口的卷积。

YOLO算法(You Only Look Once):把图片分成一些不重叠网格,每个网格对应输出中的一维anchorBox。物体中心在哪个网格则物体在哪个网格,每个网格识别中心落在该网格的物体。同样用全卷积网络,一下子算出各网格的卷积。

去掉多余定位框:两个矩形框的重叠率用IoU(Intersection over Union)表示。在物体定位时会找到大大小小多个定位框,首先去掉识别概率 < threshold的框;然后先选个概率最高的框k,去掉与k的重叠率较高的框(non-max supression,非极大值抑制),如此继续。

一个网格识别多个物体:各种形状矩形定义一个anchorBox,扩展输出标签 [anchorBox1, anchorBox2, anchorBox3…]

另:多物体的分类和定位,region-based CNN,先用分割算法找出些候选色块,再分类。

人脸识别:要训练相似度函数

Siamese网络是共享权值的两个相同网络Conv-Pool-FC,两个输出连到最后一层,训练的相似度函数。要使相同人脸的向量距离小、不同人脸的向量距离大。训练网络时是需要有同一人的多张人脸。 可以:每次同时输入三张图片,一张基准Anchor、一张相同人的Positive、一张不同人的Negative,要求\(d(A, P) + α <= d(A, N),α>0\)\(L = max( d(A, P) - d(A, N) + α, 0 )\),α限制要显著地小。 或者:每次同时输入两张图片,最后做相不相似的二元分类。\(y = σ( \sum( w * | f^{(i)}_k - f^{(j)}_k | ) + b )\)

神经风格转换:给Content图片C加上Style图片S的风格

先随机生成图片G,然后最小化代价函数,根据梯度调整G。代价函数由内容和风格两部分构成,\(J(G) = αJ_{content}(C, G) + βJ_{style}(S, G)\)。 内容代价是在隐藏层L上计算与内容图片激活情况的差距,\(J_{content}(C,G) = 1/2 * || a^{C} - a^{G} ||_2^2\)。 风格是隐藏层L(w*h*c)的c个子层间的“相关性”。因为一个子层是一种卷积核运算的结果,表示提取了某种特性。两子层间的相关性表示两种特征同时出现的情况,各子层间的相关性表示各种特征同时出现的情况,就表示了某种风格。风格矩阵(Gram矩阵):维度c*c的矩阵,每个元素表示两个子层相同位置激活值相乘的的卷积值,是一种非标准的”相关性"。风格代价是在隐藏层L上计算和风格图片的Gram矩阵的差距,\(J_{style}(S, G) = 1/(2hwc)^2 || Gram^{S} - Gram^{G} ||_2^2\)。 最后,不仅考虑某隐藏层L,所有层都可以加入到损失函数计算中,这就考虑了各层风格。


序列模型

RNN(循环神经网络)

t时刻输入\(x_t\),t-1时刻激活值\(a_{<t-1>}\)参与当前激活值计算 \(a_{<t>} = g( w_{aa}a^{<t-1>} + w_{ax}x^{<t>} + b_{a} )\),当前预测值 \(y^{<t>} = g( w_{ya}a^{<t>} + b_{y} )\)。计算激活值时的g通常选tanh、偶尔relu,计算预测值时的g根据分类问题二元sigmod、多元softmax。 \([A,B]\)表示两矩阵横向拼接、\([A;B]\)表示两矩阵竖向拼接,让\(w_{a} = [w_{ax}, w{ax}]\)\(a_{<t>} = g( w_{a} [a^{<t-1>}; x^{<t>}] + b_{a} )\)

语言模型

有个词表,单词用词表长one-hot向量表示。每次输入一个单词,输出此刻各个单词的概率。最小化代价是各时刻猜测概率与实际出现单词的交叉熵。

RNN按时间一层层展开,就是一层层深度,梯度会消失。梯度消失导致无法长期依赖,可使用门单元组件解决,如GRU、LSTM等。

GRU(Gate Recurrent Unit,门控循环单元)

记忆细胞\(c^{<t>}=a^{<t>}\)记录t时刻的激活值, 按RNN激活值计算作为候选\(z^{<t>} = w_c [ c^{<t-1>}; x^{<t>} ] + b_c\)\(c’^{<t>}=tanh( z^{<t>} )\), 一个门(简化版)控制取候选\(c’^{<t>}\)还是取前一值\(c^{<t-1>}\)\(G_u=σ( 形似z^{<t>} )\),形似\(z^{<t>}\)有自己的w、b参数 \(c^{<t>} = G_u * c’^{<t>} + (1-G_u) * c^{<t-1>}\)

因为\(G_{u}\)通常接近于0,\(c^{<t>}=c^{<t-1>}\),保持旧状态,没有梯度消失问题

LSTM(Long Short Term Memory,长短期记忆)

三个门,更新门\(G_u\)、忘记门\(G_f\)、输出门\(G_o\) \(z^{<t>} = w_c [ a^{<t-1>}; x^{<t>} ] + b_c\)\(c’^{<t>}=tanh( z^{<t>} )\)\(G_u = σ( 形似z^{<t>} )\), \(G_o = σ( 形似z^{<t>} )\), \(G_o = σ( 形似z^{<t>} )\),形似\(z^{<t>}\)有自己的w、b参数 \(c^{<t>} = G_u * c’^{<t>} + G_f * c^{<t-1>}\)\(a^{<t>} = G_o * c^{<t>}\)

词嵌入学习

用“词嵌入”表示单词,词嵌入向量表示单词与各种语义的关联度。

skip-grams:学习映射关系 context词=>附近某target词 负采样:选一个 context词=>附近某target词 作为正样本,再取一些到随机词的映射作为负样本,训练分辨映射是从附近词中选的还是随机选的 glove:统计target词在content词周围出现的次数,最小化它特定的代价函数

情感分类

词嵌入作输入,经过多对一RNN,再多元分类

seq2seq模型(序列到序列)

一个encoder网络,接一个decoder网络

beam search(定向搜索)一次搜索k个最可能结果,是扩大搜索范围的贪婪搜索 要翻译句子,首先输入句子到encoder网络,decoder网络t=0时刻输出各单词概率,beam search选其中概率最高的k个进入t=1时刻。t=1时刻在试着选定第一个词的情况下算第二个词的概率,再选其中概率最高的k个。如此继续。

注意力模型

为解决先编码再解码的中间向量瓶颈,引入注意力模型。

在decoder的某时刻t,对encoder各时刻t’的输出值\(a^{<t’>}\)的注意力分布\(a^{<t,t’>}\),加权和\(\sum( a^{<t,t’>}a^{<t’>} )\)作为decoder时刻t的输入。

注意力分布\(a^{<t,t’>}\)由decoder时刻t-1的输出、encoder时刻t’的输出\(a^{<t'>}\)之间算相关度评分,再maxsoft归一化而得。相关度评分比如可以算两者的向量cos距离,或再引入个额外神经网络计算。

详细解释参见

语音识别

音频预处理成时频谱spectrogram:横坐标时间、纵坐标频率、颜色亮度表示能量强度。 > 频谱spectrum:时频谱中垂直于横坐标的截面,横坐标是频率、纵坐标是能量强度。


参考

词向量

词的分布假说:词的特征由其上下文决定。 词向量又称词嵌入(word embedding),把词映射到某几个特征构成的低维向量。embedding在数学上表示一个映射,该映射是单射的(每个Y只有唯一的X对应,反之亦然)、结构保存的(比如在X所属空间上X1<X2,那么映射后在Y所属空间上同样Y1<Y2)。

工具word2vec可训练词向量。skip-gram从中心词预测周围词,CBOW从周围词预测中心词。

skip-gram

对每个单词,计算与前后各m个邻近词共同出现的概率积J’(θ)。loss函数用负对数似然估计J(θ)。 与邻近词\(u_o\)共同出现的概率\(p(o|c)\)用softmax表示,其中\(u_o^Tv_c\)是邻近词\(u_o\)与中心词\(v_c\)的相似度。 Lecture2课件有梯度\(\frac{\partial{J(θ)}}{\partial{v_c}}\)的推导过程。

Midterm Review有skip-gram例子讲清楚过程。

cbow
glove

skip-gram用梯度下降法逐步计算了单词之间共同出现的概率。还可以用个全局矩阵记录单词之间在一定窗口内共同出现的次数,GloVe利用了这一信息。

词窗口分类

利用前后各m个邻近词,把中心词在几个歧义中的分类。


RNN是基础构件

RNN有一个内部状态\(h_{t-1}\)、待训练参数\(W=\begin{bmatrix} W^{(hh)} & W^{(hx)}\end{bmatrix}\),每次读取输入\(x_t\),更新\(z_t=W^{(hh)}h_{t-1}+W^{(hx)}x_t=\begin{bmatrix} W^{(hh)} & W^{(hx)}\end{bmatrix} \begin{pmatrix}h_{t-1} \cr x_t\end{pmatrix}, h_t=g(z_t)\)

RNN网络深度较大,因链式求导不断相乘,有梯度消失或爆炸的问题。GRU和LSTM缓解了梯度消失问题,是常用的RNN。

GRU, Gated Recurrent Unit

\(h_t=gru(x_t, h_{t-1})\) \(\tilde{h}_t\)是候选更新,update gate \(z_t\)决定新状态中旧状态和候选更新所占比例,reset gate \(r_t\)决定候选更新中保留多少旧状态。

LSTM, Long Short Term Memory

\(h_t=lstm(x_t, h_{t-1})\)

神经机器翻译

语言模型计算整个单词序列的概率\(P(w_1,…,w_r)\)

由两个RNN分别作encoder(蓝色)和decoder(红色)。由于只记住encoder最后状态Y,翻译长句子时效果不好。 Attention的想法是多记住一些源状态。 decoder算\(h_t\)时,先算\(h_{t-1}\)与各源状态的相似性得分,用softmax将得分转成概率(这概率就叫attention),再加权相加各源状态得到上下文向量\(c_t\),用\(c_t\)计算\(h_t\)

CNN

词向量拼接成长向量,长为h词的滑动filter作卷积算\(c_i\)

TreeRNN, Tree Recursive Neural Network

结构预测:将相邻两节点用神经网络算合并分数,取得分最高的合并出新节点。

参考

神经网络

后向传播其实就是使用导数的链式法则 计算图节点可以按不同粒度组合

卷积神经网络

过程:用filter卷积算得新图层,多个filters算得的图层叠成新图(卷积层),新图经ReLU处理;连续Conv+ReLU弄几层,偶有Pool层降分辨率;最后把输出展成一维向量,接入全连接层(神经网络)做多元分类。

卷积层: 连续Conv+ReLU弄几层: 偶有Pool层降分辨率: 抽样方法比如: 最后把输出展成一维向量,接入全连接层:

典型的架构像这样: [(Conv-ReLU)*N-POOL?]*M-(FC-ReLU)*K-SoftMax

循环神经网络

各时刻共享权重参数W,由前一刻状态\(h_{t-1}\)、当前输入\(x_t\)计算新状态\(h_t\)、计算预测值\(y_t\)、计算\(loss_t\);序列x全读完了总loss就是\(\sum loss_t\),再后向传播计算梯度(backprop through time)、更新W。

实践中,简单RNN难以描述长期依赖。更常用LSTM(Long Short Term Memory)、GRU(Gated Recurrent Unit),GRU比LSTM计算更少、效果相当。


多分类的loss函数

max margin

要让正确分类\(s_{y_i}\)比错误分类\(s_j\)的分数大(具体大多少无所谓,这里取大1)。满足这条件时loss为0、不满足时loss为\(s_j-s_{y_i}+1\)

softmax

常见激活函数

sigmoid的导数在[0, 0.25],tanh的导数在[0, 1] ReLu当学习率大时梯度减得多,容易x<0变为dead。可减小学习率,或者使用Leaky ReLu、ELU。

一些调优问题

train/validation/test

train集用于超参数选定后的模型训练,validation集用于验证超参数效果,test集只在最后出结果时用一次。交叉验证只在小数据集时有用。

初始化权重矩阵W

初始随机值太小或太大都不好。比如tanh作激活函数,若初始值太小,几次迭代后输出0。若初始值太大,函数饱和梯度=0。

可用Xavier初始法,W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in)

若ReLU作激活函数,可用W = np.random.randn(fan_in, fan_out) / np.sqrt(fan_in/2)

Batch Norm

训练时使用mini-batch的μ和σ修正数据:先让μ归0、σ归1,再加个BN层训练正态分布的缩放γ和平移β;测试时使用全部mini-batch上的移动平均μ和移动平均σ修正数据。

随机梯度下降陷入局部极小值或鞍点

使用“Momentum”——移动平均梯度 更常用Adam优化 = Momentum + AdaGrad/RMSprop

dropout正则化

随机关闭每层一定比例的节点,强制丢掉一些信息防止过拟合。

迁移学习

迁移到小数据集时,重训最后一层。迁移到较大数据集时,重训最后几层。


语义分割

全卷积层,先downsampling再upsampling

转置卷积:一种upsampling方法

待upsampling的像素值、乘上filter、累加到输出的对应位置。 从矩阵乘法角度看

单个物体同时分类和定位

定位就是训练固定个数的定位参数,当作回归问题。如下面猫框有4个参数、姿势的关节位置有14个参数。

多个物体的分类和定位

R-CNN(Region-based CNN):要先用语义分割找出一些候选区域,再看这些区域是否包含关注列表中的物体并定位。

图片变成特定风格

内容相似:使生成图的各层激活情况尽量匹配原图。 风格相似:使生成图的各层gram矩阵尽量匹配原图各层。


非监督学习

生成模型

生成符合训练集样本分布的新数据

变分自编码器(Variational AutoEncoder)

参见Kevin Frans的解释,VAE就是给AutoEncoder里连接encoder和decoder中间的那个潜在变量添加限制,限制它符合单位正态分布。实现上训练出μ和σ,再从它们抽样出正态分布的潜在变量,就是下面的z。损失函数由两部分组成:潜在变量和单位正态分布的拟合度(KL散度),生成图像与输入图像的拟合度(均方误差)。

生成对抗网络(Generative Adversarial Network)

对抗博弈,generator生成假图片,discriminator区分真假图片。


强化学习

跟环境互动,最大化奖励。数学形式就是Markov决策过程。

Q-learning

使用神经网络拟合Q函数,因为Q-Learning表格维度巨大难以实用。

Policy Gradient

参考

Michael Nielsen的入门教程,从零开始构建一个识别mnist手写数字的神经网络。

反向传播

反向传播就是链式求导过程,训练过程拆细了说有四个方程。这里引入记号\(\delta^{(l)}=\frac{\partial C}{\partial z^{(l)}}\),即“误差”\(\delta^{(l)}\)是关于未激活加权值\(z^{(l)}\)的偏导数。又\(a^{(l)}=g(z^{(l)})\)\(z^{(l)}=w^{(l)}a^{(l-1)}+b^{(l)}\),权重\(w^{(l)}\)的维数是{第l层neuron数} x {第l-1层neuron数}

  • 输出层误差:\(\delta^{(L)}=\frac{\partial{C}}{\partial{a^{(L)}}} \frac{\partial{a^{(L)}}}{\partial{z^{(L)}}}=\frac{\partial{C}}{\partial{a^{(L)}}} g\prime(z^{(L)})\)
  • 其他层误差\(\delta^{(l)}\)用下一层误差\(\delta^{(l+1)}\)表示:\(\delta^{(l)}=\frac{\partial{C}}{\partial{z^{(l+1)}}} \frac{\partial{z^{(l+1)}}}{\partial{a^{(l)}}} \frac{\partial{a^{(l)}}}{\partial{z^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l+1)}}} \frac{\partial{(w^{(l+1)}a^{(l)}+b^{(l+1)})}}{\partial{a^{(l)}}} \frac{\partial{a^{(l)}}}{\partial{z^{(l)}}}=(w^{(l+1)})^T \delta^{(l+1)} g\prime(z^{(l)})\)
  • 层权重的偏导数:\(\frac{\partial{C}}{\partial{w^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l)}}} \frac{\partial{z^{(l)}}}{\partial{w^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l)}}} \frac{\partial{(w^{(l)}a^{(l-1)}+b^{(l)})}}{\partial{w^{(l)}}}=\delta^{(l)}(a^{(l-1)})^T\)
  • 层偏置的偏导数:\(\frac{\partial{C}}{\partial{b^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l)}}} \frac{\partial{z^{(l)}}}{\partial{b^{(l)}}}=\frac{\partial{C}}{\partial{z^{(l)}}} \frac{\partial{(w^{(l)}a^{(l-1)}+b^{(l)})}}{\partial{b^{(l)}}}=\delta^{(l)}\)

由这四个方程就能写出backprop()代码,见network.py

训练过程的改进

用交叉熵代价函数

若反向传播时对权重w和偏置b的梯度太小就学习缓慢,看上面方程知这两梯度跟\(\delta^{(l)}\)有关。考查输出神经元,\(\delta^{(l)}\)就是\(\delta^{(L)}=\frac{\partial{C}}{\partial{a^{(L)}}} g\prime(z^{(L)})\)

使用L2代价函数 \(C=\frac{1}{2}\sum_j(y_j-a_j)^2\)、sigmoid激活,\(\delta^{(L)}=(a^{(L)}-y) g\prime(z^{(L)})\),跟sigmoid斜率有关,sigmoid饱和时学习缓慢。

可用交叉熵 \(C=-\frac{1}{n}\sum\limits_x{[y\ln a+(1-y)\ln(1-a)]}\) 作代价函数,因为满足非负、且网络表现好(预测值a接近实际值y、y为0或1)时代价接近0。使用交叉熵、sigmoid激活,\(\delta^{(L)}=a^{(L)}-y\),无学习缓慢问题。

多分类时用softmax作输出层的激活函数,输出可理解为概率分布。可用对数似然 \(C=-\ln a_y^{(L)}\) 作代价函数,因为满足非负、且网络表现好(\(a_y^{(L)}接近1\))时代价接近0。使用对数似然、softmax激活,\(\delta^{(L)}=a_j^{(L)}-y_j\),无学习缓慢问题。

规范化防止过拟合

神经网络有大量参数,很容易就过拟合。训练集和测试集上的分类准确率相差越大,过拟合越严重。减轻过拟合的方法,除了增加训练样本、减小网络规模,还有规范化。规范化即在代价函数上添加额外的惩罚,惩罚不涉及偏置。比如L2规范化,添加权重惩罚\(\frac{λ}{2n}\sum\limits_w{w^2}, λ>0\),参数λ越大越倾向小的权重;同时,规范化反向传播给权重偏导数添上\(\frac{λ}{n}w\),梯度下降变成\(w=(1-\frac{αλ}{n}w)-\frac{α}{m}\sum\limits_{x}\frac{\partial{C_x}}{\partial{w}}\)(n是训练集大小,m是minibatch大小)。

规范化的本质是引入一些随机性,Dropout也算一种规范化。在小批量数据上的“前向反向”传播开始前,各隐藏层都随机关闭一定比例的神经元,在剩余子集中做一遍“前向反向”传播;最终选出在不同随机子集中都更健壮的特征。因为权重和偏置是在隐藏层部分神经元关闭的情况下学到的,而实际运行时隐藏层所有神经元都打开,使隐藏层的输出变大。为了补偿这个,将(隐藏层的输出)x(隐藏层神经元参与训练的比例)。

权重初始化

正态分布独立变量的和:如果\(X \sim N(μ_X, σ_X^2), Y \sim N(μ_Y, σ_Y^2), Z=X+Y\),那么\(Z \sim N(μ_X+μ_Y, δ_X^2+δ_Y^2)\)

考虑某个隐藏神经元,其\(z=\sum_j{w_jx_j}+b\),激活函数是sigmoid。若我们用\(w \sim N(0,1)\)初始化权重,简单想\(x_j=1, b=0\)时,z就是\(n_{in}\)个输入\(w \sim N(0,1)\)相加,\(z \sim N(0,n_{in})\)。z的方差较大、取值范围较大,容易过饱和、学习缓慢。若我们将正态分布初始值除以\(\sqrt{n_{in}}\),即标准差缩小到\(\frac{1}{\sqrt{n_{in}}}\),则\(w \sim N(0, \frac{1}{n_{in}})\)\(z \sim N(0,1)\),避免了过饱和问题。

若激活函数是ReLU,因为其左半是“死区”,平均算\(n_{in}/2\)个有效输入,要将正态分布初始值除以\(\sqrt{n_{in}/2}\)

合适的权重初始化可以让学习速度更快。偏置初始化则没有影响,任意值都行。

交叉熵、规范化、权重初始化改进后的代码,见network2.py

含动量的梯度下降

梯度不直接更新权重,引入“速度”的概念,梯度(加速度)影响速度、速度影响权重(距离)。这样如果在某个梯度方向持续更新,该方向上速度叠加,权重更新得更快。

设置超参数
  • 训练集上代价不再下降,而是震荡或增加,学习率太大
  • 验证集上分类准确率n回合不提升,提前终止训练回合,或尝试减小学习率
  • 规范化参数先设为0,选个学习率,再选规范化参数,再调学习率
  • 小批量数据的大小设置相对独立

深度网络的不稳定梯度问题

因为反向传播就是链式相乘,网络越深链条越长,前面层因乘积中大量的项导致不稳定的梯度(消失或激增)。

RNN中的长短期记忆LSTM,就为了解决不稳定梯度问题。

参考

Spark软件栈构成个集群计算平台。SparkCore对运行在集群上的由很多计算任务组成的应用进行调度、分发和监控。

Spark对数据的抽象叫RDD(Resilient Distributed Dataset,弹性分布式数据集),是不可变的分布式对象集合,叫“弹性”是因为任何时候都能重算。RDD分成多个分区,分别运行在集群的不同节点上。RDD支持两种操作:transformation和action。转化操作由RDD生成新RDD,如map()和filter(),惰性求值,即第一次在行动操作中用到时才真正计算。行动操作对RDD计算一个结果,如reduce(),默认每次都重新计算RDD,除非RDD显式持久化,默认持久化到JVM堆空间。

键值对RDD就是Java的Tuple2或Python的元组,可支持操作reduceByKey()、groupByKey()、join()、sortByKey()、countByKey()等

Spark支持多种输入输出源:

  • 本地或分布式文件系统:文本文件、JSON、CSV、SequenceFiles(用于键值对数据的Hadoop文件格式)、Protocol Buffers、Java序列化出的对象文件
  • Spark SQL:Hive支持的任何表(Apache Hive是Hadoop中常见的结构化数据源)
  • JDBC数据库、Cassandra、HBase、elasticsearch

参考

  • 《Spark快速大数据分析》

重要的基础库

numpy

多维数组对象ndarray,向量化数组运算,线性代数运算、傅里叶变换、随机数生成

作为算法之间传递数据的容器,numpy数组对数值型数据的存储和处理要比python内置的高效

pandas

Series是一维数据及其索引,类似数组(整数索引)或定长有序的字典

DataFrame是二维表格,既有列索引又有行索引

scipy

解决科学计算中各种标准问题的一组包,主要包括:

  • integrate:解微积分方程
  • linalg:扩展了由numpy.linalg提供的线性代数运算、矩阵分解功能
  • optimize:函数优化器(最小化器)、根查找算法
  • signal:信号处理工具
  • sparse:稀疏矩阵和系数线性系统求解
  • special:许多常用数学函数(如伽玛函数)
  • stats:连续或离散概率分布(如密度函数、采样器、连续分布函数)、各种统计检验方法、描述统计法
  • weave:利用内联c++代码加速数组计算

iPython环境

粘贴剪贴板内容用%cpaste

输入行In[X]的文本保存在_iX变量中,输出行Out[X]的文本保存在_X变量中,最近两个输出结果保存在_(一个下划线)和__(两个下划线)变量中

可与系统shell交互:output = !cmd $arg

算语句执行时间用%timeit

使对象在Out[]输出时更有意义,给类加上__repr__方法

NumPy基础

arr.shape说明数组各维度大小,arr.dtype说明数组数据类型

np.arange(n)产生[0,n)序列

np.reshape((m, n))重排成m行n列数组

切片和索引

ndarray切片是原数组的引用,切片上任何修改都会直接反映到原数组上。这跟python数组切片总是复制数据不同。要想获得ndarray切片的副本需要显式地复制,如arr[5:8].copy()

arr[i,j]等价于arr[i][j];多维数组中,若省略了后面的索引,会返回维度低些的ndarray

布尔索引:索引中的数组作比较运算,将得到一个布尔数组作为索引。多个布尔数组可以使用 与&、或|、非- 运算。将创建数据的副本。

花式索引:用整数数组或ndarray作索引。将创建数据的副本。

使用np.ix_函数选取矩形区域,如arr[np.ix_(rows, columns)]


np.where(cond, xarr, yarr)if cond then xarr else yarr的向量化缩写

arr.sort()就地排序,np.sort(arr)返回排序副本

线性代数

mat.T转置

matA.dot(matB)矩阵相乘,奇怪的命名!

solve解Ax=b,其中A为方阵

lstsq求Ax=b的最小二乘解

pandas入门

由字典构造DataFrame时,字典一项变为DataFrame一列

索引

DateFrame相关参数中columns索引列,index索引行

frame[]式索引:

  • 用标签是取列(标签切片是闭区间)
  • 用数字切片或布尔数组是取行
  • 用布尔DataFrame是取元素

frame.ix[]选取行列:

  • frame.ix[rows, columns]选取行和列,ixindex缩写
  • frame.ix[rows]选取行
  • frame.ix[:, columns]选取列

由整数索引的pandas对象,根据整数选取数据的操作总是面向标签的,这也包括用ix进行切片


默认axis = 0按行,一行行操作或所有行聚合;axis = 1按列,一列列操作或所有列聚合

sort_index()排序:

  • frame.sort_index()按行标签排序
  • frame.sort_index(axis = 1)按列标签排序
  • frame.sort_index(by=['a','b'])按某些行列的值排序

combine_first(frame1, frame2),先从第一个对象中取值,不行就再去第二个对象中取值

层次化索引,一个轴上有多级索引。swaplevel(level1, level2)互换两个级别,sortlevel(level)根据某级别的值对数据排序。

set_index()将某些列转换为行索引,reset_index()将行索引转换为列

stack将列索引旋转为最内层行索引,unstack将行索引旋转为最内层列索引

pivot透视

将只分一些列名不断append的“长格式”,转换为以某列值为行索引的“宽格式”

frame.pivot(作为行索引的列名, 作为列索引的列名, 作为单元格的列名); 如果忽略最后一个参数,frame.pivot(作为行索引的列名,作为列索引的列名),其他列会分别作为外层列索引得到一个个DataFrame,这些DataFrame一个个横向拼接起来,得到带两层列索引的DataFrame

其实,pivot只是一个快捷方式:用set_index创建(行索引名, 列索引名)的两层行索引,再用unstack将内层的(列索引名)转回成列索引


groupby是一个 拆分-应用-合并 的过程:将数据根据特定轴的一个或多个键拆分为多组,在分组上应用函数产生一个新值,将所有分组的结果合并

pivot_table按行列分组聚合,对比pivot是只分组不聚合

crosstab按行列分组计数,是特殊的pivot_table

以时间戳(字符串或datetime对象)为索引的Series就是时间序列,有多种时间选择方式

画图入门

matplotlib是基础

在Figure的subplot上绘图

画线时,颜色(k黑r红 等,或#rgb值)、标记(o圆x叉 等)、线型(--虚线.点线 等)


seaborn接口更高级

scikit-learn

模型验证

5-fold cross-validation


模型复杂度

注意:训练效果总是好于预测效果


high-variance模型的样本大小

常用模型

naive bayes 的 的naive就naive在 要先假设样本属于某个特定分布,最终的分类效果也只是趋于这个分布,常作为分类任务的初始尝试


简单线性回归 \(y = a_0 + a_1x_1 + a_2x_2 + a_3x_3 + ...\),常作为回归任务的初始尝试

多项式回归 \(y = a_0 +a_1x + a_2x^2 + a_3x^3 + ...\),可使用线性回归库计算,只要根据\(x_n = x^n\)先把输入\(x\)变换成 \(x, x^2, x^3, ...\)。其他非线性回归类似,只要根据\(x_n = f_n(x)\)先把输入\(x\)变换即可。

参考

线性代数围绕两种基本运算:向量加法和向量数乘

向量:都指列向量。向量可看作是,把基向量i^, j^, …分别按照向量各数值缩放后的和

生成空间:一组向量线性组合后所能到达的点的集合

线性相关:如果某个向量落在其它向量的生成空间内,这个向量就与其它向量线性相关

线性变换:保持网格线平行且等距分布 的原点不变变换

左边乘矩阵M:跟线性变换等价,把基向量i^, j^, …分别变换成A的各列向量

行列式:线性变换的空间(二维面积或三维体积)缩放比例,负值表示空间被翻转(正向依右手定律)

行列式为0:线性变换将空间压缩到更小的维度上

解线性方程组,就是要找未知向量x,x经过A变换后将变成v。要解线性方程组,需要看变换A是否将空间压缩到了更小的维度上,也就是看det(A)是否为0。如果det(A)≠0,不压缩空间,A的逆变换存在,\(x = A^{-1} * v\)。如果det(A)=0,压缩空间,逆变换不存在,x有解仅当v正好落在压缩后的空间内。比如说A将平面压缩为一条直线,x有解仅当v正好落在这直线上。

特别地,对于线性方程组Ax=0,x讲过A变换后变成零空间。若A不压缩空间,只能x=0。若x有非零解,只能A压缩空间,det(A)=0。

若变换A压缩空间,肯定有一个过原点的直线(降一维)或平面(降两维)被压缩到原点,这个被压缩的直线或平面就叫矩阵A的”零空间”或”核”,也是方程组Ax=0的解空间

秩:变换后(可能降维)的空间维数叫做变换的

满秩矩阵:变换后不降维

非方阵:mxn矩阵表示从n维空间到m维空间的变换

点积:两个向量的点积就是 一个向量在另一个向量上的投影长度 * 另一个向量的长度,方向不同时取负号

叉积:等价于 列向量矩阵的行列式(正向依右手定律)

基变换:其他基组在本坐标系下构成列向量矩阵P,P表示从其他基到本基的变换

相似矩阵:表达式 \(P^{-1} * A * P\) 代表同一变换的一种视角转换,中间矩阵A代表你所见的变换,外侧两矩阵代表视角的转换。\(P^{-1} * A * P\) 叫做相似矩阵,与A代表着同一个变换,只不过是从其他基的角度来看。总结就是,相似矩阵是同一变换的不同描述矩阵。

特征向量和特征值:线性变换后仍停留在原先直线上只被拉伸或压缩的向量叫做特征向量,拉伸或压缩的比例叫做特征值

特征基:若线性变换M有足够的特征向量,可从中选出能张成全空间的一些特征向量作为基,则在特征基视角下的相似矩阵 \(P^{-1} * A * P\) 为对角矩阵(因为特征基只被拉伸或压缩),这种“相似对角化”可以简化计算

参考

监督学习

数据集已有正确答案,预测新数据的答案。预测连续值就是回归问题,预测离散值就是分类问题。

模型、代价函数

假设模型可以用h函数表示但参数未知,机器学习使用训练集为h训练参数,算出参数后的h就可以做些预测。

要训练得到怎样的参数?要得到使代价函数最小化的参数。 代价函数是关于这些未知参数的函数。比如线性回归的h函数是关于n维特征的多项式\(h_θ(x)=θ_0+θ_1x_1+θ_2x_2+...+θ_nx_n=θ^Tx\),可选平方误差作代价函数}$、 \(y^{(i)}\)指第i个样本的值。

梯度下降法

求最小化代价函数的参数,就是求函数取极值时的变量值。可用梯度下降法来迭代逼近局部最小值,当代价函数是凸函数时局部最小值就是全局最小值。所谓梯度,是指包含所有一阶偏导数的向量。

比如上面的线性回归问题,梯度下降法初始时先随便取组θ值,每次迭代就是各个维度都减去一点自己维上的梯度。相当于先在J(θ)的n+1维曲面上随便取个点,然后取周围使n+1维梯度下降最快的那个点,一步步迭代达到局部极小值。

比如2维的, 其中的α叫学习率,α太小下降太慢,α太大可能越过最低点来回振荡无法收敛。

当有多个特征时,各特征的取值范围可能差别很大,在用梯度下降前要先把各特征值归一到较小区间,比如[-1,1]。方法比如将原先的\(x_i\)换成\(\frac{x_i-μ_i}{range(x)}\)\(\frac{x_i-μ_i}{std(x)}\)

梯度下降每次迭代要在所有m个样本上计算,速度太慢。实践中先把样本顺序打乱,然后每次在b个样本的minibatch上梯度下降,这就叫随机梯度下降。下图中b=1。

方程求解法

直接求解训练集m个样本构成的方程组。把m个样本的特征数据排成m*(n+1)的矩阵X(注\(x_0=1\)),m个样本的结果数据排成m*1的向量y,则\(Xθ=y, X^TXθ=X^Ty, θ=(X^TX)^{-1}X^Ty\)。之所以先\(X^TX\)可能为了先转成方阶再求逆。Octave代码pinv(X'*X)*X'*y,pinv是求伪逆。当然,向量化时也可把m个样本的特征数据排成(n+1)*m的矩阵,m个样本的结果数据排成1*m的向量y,然后\(θ^TX=y\),这样就跟单样本时\(θ^Tx=y\)形式相同。

方程法不需要选择学习率α,不需要将多特征的特征值归一到较小区间;缺点是求逆运算\(O(n^3)\),n不能太大。


二元分类

就是逻辑回归,用sigmoid函数将线性回归的值域变成(0,1),值>=0.5取y=1、值<0.5取y=0,这样就成了二元分类。

也就是说,逻辑回归模型的h函数是\(h_θ(x)=g(θ^Tx)\),其中\(g(z)=\frac{1}{1+e^{-z}}\)叫sigmoid函数或logistic函数,h(x)表示y=1的概率。对二元分类,由sigmoid曲线知,\(θ^Tx>=0\)取y=1,\(θ^Tx<0\)取y=0,\(θ^Tx\)就叫决策边界。 sigmoid性质:\(g'(z)=\frac{d}{dz}g(z)=g(z)(1-g(z))\)

逻辑回归的代价函数定义如下,可以保证为凸函数: 其中\(h_θ(x^{(i)})\)为样本预测值,\(y^{(i)}\)为样本实际值。合成一个,01分布的交叉熵: 同样,需要得到使代价函数最小化的未知参数θ。可先算出一阶偏导数构成的梯度向量,然后用梯度下降法求解。

扩展到多元分类

为每个类训练一个“属于该类而不属于其他类”的“一对余”二元分类器,训练集的标签向量y要先转变成one-hot向量参与训练。要预测新数据属于哪一类,先计算新数据属于各个类的概率,取概率最大的那个类。


正则化

当特征太多时,模型容易太复杂、容易过拟合。为了防止过拟合,可以人工去掉一些不重要的特征,可以保留所有特征但减小特征参数\(θ_j\)(正则化)。

正则化范数惩罚\(θ_j\), j=[1..n]但不罚\(θ_0\)。在代价函数中加上\(\frac{λ}{2m}\sum_{j=1}^{n}θ_j^2\),λ越大则最终\(θ_j\)越小。λ叫正则化参数,这里除以2m是为了以后计算时方便。

线性回归和逻辑回归的梯度计算相同 梯度下降法也相同 方程求解法正则化后变成


神经网络

处理非线性分类需要非线性的h函数,否则多项式h函数的特征数将指数级爆炸。

神经网络把逻辑单元(模拟神经元)按层组织。每个逻辑单元是个二元分类(逻辑回归),输入是一个总为1的偏置单元和上一层所有的逻辑单元。这样每层就是个多元分类,整个神经网络就是一层层的多元分类。

计算梯度的过程先前向传播计算各层输出、再反向传播计算各层梯度,反向一层层梯度计算其实就是“计算图”的导数链式计算。

前向传播计算各层输出

\(a^{(l)}\)表示第l层输出(作为下一层输入时要记得添上那总为1的第一分量),\(Θ^{(l)}\)表示第l层到下一层的系数矩阵({下一层单元数}行 x {这一层单元数+1}列),设\(z^{(l)}=Θ^{(l)}a^{(l)}\),则\(a^{(l+1)}=g(z^{(l)})\)

反向传播计算各层梯度

前向传播在知道一层到下一层的系数矩阵时计算下一层的值,但各层系数矩阵是未知的,需要我们通过最小化代价函数训练出来。神经网络的代价函数,是最终层逻辑单元的代价函数和(加上前面各层系数矩阵的正则化惩罚)。要最小化代价函数J(Θ),需求得各层梯度,即各层系数矩阵的偏导数\(D_{ij}^{(l)}=\frac{\partial}{\partialΘ_{ij}^{(l)}}J(Θ)\)。反向传播反向计算各层误差,最终算出各层梯度。这里引入记号,把J(Θ)关于\(z_j^{(l)}\)的偏导数\(δ_j^{(l)}\)叫做“误差”。

反向传播的过程:对每一个样本,先前向传播计算各层输出\(a^{(l)}\),再从最后一层往回倒,反向计算各层误差\(δ^{(l)}=(Θ^{(l)})^Tδ^{(l+1)} .* g’(z^{(l)}), g’(z^{(l)})=a^{(l)}.*(1-a^{(l)})\)并累加\(Δ^{(l)} = Δ^{(l)}+δ^{(l+1)}(a^{(l)})^T\),初始误差\(δ^{(L)}=a^{(L)}-y\)。处理完所有样本后,偏导数取平均值\(D_{ij}^{(l)}=\frac{1}{m}Δ_{ij}^{(l)}\) if j=0,\(D_{ij}^{(l)}=\frac{1}{m}Δ_{ij}^{(l)}+\frac{λ}{m}Θ_{ij}^{(l)}\) if j≥1。

注:初始化系数矩阵要用[-ε,ε]的随机小值。若用相同值将导致下层各逻辑单元毫无差别,若用大值将使sigmoid值接近1或0,斜率小梯度下降收敛慢。 后向传播计算结果可近似验证\(\frac{d}{dθ_i}J(θ) ≈ \frac{J(θ_1,…,θ_i+ε,…,θ_n)-J(θ_1,…,θ_i-ε,…,θ_n)}{2ε}\)

关于激活函数

这里用sigmoid函数作例子,实际上可以为各层选择不同的激活函数。比如输出层还用sigmoid作二元分类;隐藏层可用tanh函数(相当于sigmoid平移缩放过原点),或更常见的ReLU函数g(z)=max{0,z}(Rectified Linear Unit,Geoffrey Hinton已证明ReLu几乎等同于一叠logistic单元)。 sigmoid函数:\(g(z)=\frac{1}{1+e^{-z}}, g'(z)=g(z)(1-g(z))\) tanh函数:\(g(z)=\frac{e^z-e^{-z}}{e^z+e^{-z}}, g'(z)=1-g^2(z)\) ReLU函数:\(g(z)=max(0,z), g'(z)=\begin{cases} 0& \text{if x<0}\\ 1& \text{if x≥0}\end{cases}\) leaky ReLU函数:\(g(z)=max(0.01z, z), g'(z)=\begin{cases} 0.01& \text{if x<0}\\ 1& \text{if x≥0}\end{cases}\)


支持向量机

非线性的h函数不知啥样不妨碍。只要知道代价函数J(θ)、算出梯度、训练出θ,就能预测 y=1 if \(θ^Tx≥0\)

SVM的代价函数可从逻辑回归的代价函数推广得到: 1. 将代价函数从曲线变成分段线性的\(cost_1(z)\)\(cost_0(z)\) 2. 要最小化J(θ),m倍数无所谓,A+λB变成CA+B形式(C=1/λ),就得到

SVM是一种大间距二元分类器,最小化J(θ)造成决策边界在类间保持较大间距。 异常点存在时,小C值的间距大。

SVM是一种线性分类器,但实践中可以构造新特征并在新特征的维度上线性可分,这样就可应用于非线性问题。

核函数

把a维特征通过某种变换变成b维特征,这变换就叫kernel(核函数)。SVM通常用核函数把数据点映射到高维空间,低维空间上不好分,高维空间上容易分。 如图中,通过3个landmark把2维特征x变成3维特征f,这里similarity函数就是kernel。 具体来说,这里用了高斯kernel:\(f_i=similarity(x,l^{(i)})=exp(-\frac{||x-l^{(i)}||^2}{2σ^2})\)。当x接近\(l^{(i)}\)\(f_i≈1\);当x远离\(l^{(i)}\)\(f_i≈0\);带宽σ越大,x远离\(l^{(i)}\)\(f_i\)越慢下降到0。

由于涉及多维特征,kernal变换前记得先特征值归一化。kernel变换后,最小化新特征f的代价函数来训练θ,对新数据x先转换成f表示后再预测 y=1 if \(θ^Tf>=0\)

实践中若有m个样本x,就每个样本作一个landmark,得m个landmark。也就是说,把特征x变换成m维新特征f。


非监督学习

把无标签数据集分簇。

k-means

优化目标是最小化所有点与它们簇中心的距离平方和: 算法的两步正是固定μ优化c、固定c优化μ:

簇数K通常是看数据分布后手动设置,初始化的K个簇中心是随机选的K个数据点。运行时如果某簇中心没有分配到数据点,这簇中心通常直接删掉,偶尔也可重新随机化。为防止k-means陷入局部最优解,要多次运行k-means取最小代价函数值。

特征降维:主成分分析

把n维点投射到k维面,最小化所有点与投射面的距离平方和\(\frac{1}{m}\sum_{i=1}^{m}||x^{(i)}-x_{approx}^{(i)}||^2\)

在训练集计算协方差矩阵Sigma,将Sigma奇异值分解,取矩阵U的前k列。然后不管训练集验证集测试集,投射X到Z用\(Z=XU_k\),从Z倒推回X用\(X≈ZU_k^T\)

怎么选择参数k?

异常检测

选出有助于异常检测的n个特征,每一维特征用训练集算出\(μ_j\)\(σ_j^2\),然后对新数据点累乘所有特征的正态分布概率,总概率太小就是异常点。 常识:正态分布有68%(稍大于2/3)的面积在μ±δ范围内。

为什么不用分类算法?因为训练数据中异常数据太少、异常类型太多,分类算法无法识别出异常特征。

怎么选出有助于异常检测的特征?用直方图\(hist(x_i)\)看特征\(x_i\)的分布,若不接近正态分布,可尝试变换特征使\(f(x_i)\)接近正态分布,\(f(xi)\)变换可以是\(log(x_i+c)\)\(x_i^{\frac{1}{t}}\)等。也可对某异常创建特征,使异常点特征值远离均值(特别大或特别小)。

推荐系统

假设我们有一份用户对电影的部分评分表,在左边排列着电影名,在上边排列着用户名。对第j列第i行,\(r(i,j)\)表示用户j是否对电影i评过分,\(y^{(i,j)}\)表示用户j对电影i的评分。

有了这个评分表,我们能知道什么?

关键要理解两个隐藏变量:电影i的特征\(x_i\)(假设有n维)、用户j的偏好\(θ_j\)(维度同\(x_i\)一样)。如果知道这两个变量,用户j对电影i的评分就为\(θ_j^Tx_i\)。或者说,所有电影特征按行排列成矩阵X,所有用户偏好按行排列成矩阵Θ。如果知道这两个矩阵,用户对电影的评分矩阵就为\(XΘ^T\)。 如果这两个矩阵只知一个,结合评分表就能线性回归训练出另一个。我们把已知电影特征X求用户偏好Θ的过程,叫做基于内容的推荐。 如果这两个矩阵都不知道,可先随机赋值、再通过X→Θ→X→Θ→…的迭代求得收敛。实际上,我们能在一步迭代中同时收敛X和Θ,这种同时迭代X和Θ的过程叫做协同过滤

协同过滤

代价函数对所有有评分的项(i,j)求和。 梯度,对x或θ的第k维特征求偏导。

若新用户对所有电影都没评分,代价函数最小化会算得该用户偏好为零向量,这没什么用。可先在评分矩阵的每一行减去该行均值,再在新评分矩阵上训练X和Θ,最后算得的预测值再加回均值。

找相似的电影?\(||x_i-x_j||\)距离越小越相似。

参考

浮动

元素浮动就像上浮到上一图层

假设块元素A是浮动的,如果A在DOM中的上一元素是普通流中元素,那么A的顶部和上一元素的底部对齐,也即A的相对垂直位置不变;如果A的上一元素也是浮动的,那么A会跟在上一元素的后面,一行放不下时放到下一行。

清除浮动时要记住:clear属性只能影响使用clear属性的元素本身

块元素在普通流中竖向排列,在浮动之后可理解为横向排列,清除浮动可理解为打破横向排列

浮动元素的原本目的是实现文本环绕,它没有高度不在普通流中,但表现得就像是多行的inline-blocks,占着宽度使文字和其他inline-block环绕。

闭合浮动

使浮动元素的父元素像只包含普通流那样高度表现正常。方法有两种:

  1. 给父元素添加.clearfix类,在.clearfix:after伪元素上设置clear属性。这种方法更好
1
2
3
4
5
.clearfix:after {
  content: ' ';
  display: block;
  clear: both;
}

clear属性表明该元素的哪一边不能有浮动元素

原理是:在元素的margin-top之上添加额外的clearance间隔,使元素盒子最终落在浮动元素的下方,并由这clearance撑起了父元素的高度。

上述方法第一个浮动元素的的top-margin,会越过父元素的border,与父元素前一元素的bottom-margin重合。要像下面的BFC方法那样使父元素表现为一个独立的盒子,可用这个版本:

1
2
3
4
5
6
7
8
.clearfix:before,
.clearfix:after {
content: ' ';
display: table;
}
.clearfix:after {
clear: both;
}
  1. 触发父元素的BFC来闭合浮动,比如设置父元素的overflow:hidden。这种方法总有副作用

BFC(Block Formatting Context,css3中叫flow root),就是个独立的盒子,BFC里面元素的布局不会影响外面的元素,而BFC自身是普通流的一部分。触发BFC可以通过设置:

  • overflow: hidden|scroll|auto
  • float:left|right 或 position: absolute|fixed
  • display: inline-block|inline-table|tabel-cell|table-caption

BFC是个独立的盒子,具体包括:

  • margin重叠不能跨越BFC内外
  • float元素不能跨越BFC内外

定位

只有三种定位:普通流、浮动、绝对定位,浮动或绝对定位的元素都会脱离普通流

absolute定位的基准是最近的positioned祖先(position默认值static,非static值都认为元素已positioned)。所以一般在设置元素posotion:absolute前,先找个祖先元素设置position:relative

relative定位不影响元素在普通流中占据的位置,相对于正常位置定位显示。

fixed定位是特殊的absolute定位,相对于浏览器视窗定位

行内元素

line-height指文本行基线之间的距离

行框的构造:

  1. 非替换元素的font-size决定内容区高度,line-height值减去font-size值就是行间距,行间距的一半分别加到内容区的顶部和底部构成行内框替换元素(如<img><input>等)的 height/padding/border/margin都加起来构成行内框高度
  2. 将各元素的基线与行基线对齐,替换元素的基线在其margin-bottom下边缘
  3. 指定了vertical-align的元素对其行内框作垂直偏移
  4. 最高行内框顶端和最低行内框底端之间构成行框

替换元素的padding/border/margin都起作用,影响行内框高,但line-height值不变(在垂直对齐时有用)。对非替换元素,左右方向的padding/border/margin起作用;上下padding/border尽管视觉上有影响,但不影响行内框的高度(=内容区高+行间距),不影响行框的构造;上下margin不起作用。

行内元素基本上会作为一行放置,然后分成多个部分。可以把行内元素想成是一个纸片,外围有一些塑料边。在多行上显示行内元素就像是把一个大纸片剪成一些小纸片,每个小纸片上不会增加额外的塑料边,所以看上去只是原来纸片(行内元素)的最前和最后两端出现塑料边。

inline-block作为替换元素放在行中,默认是基线对齐的。用来布局时要用vertical-align:top|bottom|middle显式对齐一下,否则内容不同将无法对齐:无文字时基线是容器的margin-bottom下边缘,有文字时基线是最后一行文字的基线。


References

docker engine

包含client和daemon,client(如docker命令)通过restful接口与daemon通讯

daemon管理着3种资源: - image,只读,分层,是程序运行的“源文件” - registry,保存image的地方 - container,在隔离用户空间运行的程序

注:docker machine用来设置运行着engine主机,以后不用了,用docker Mac代替

docker image

union mount把许多filesystem一层层mount到一起。container在启动装载了image的所有层后,在最上面会再mount一层可读写filesystem,我们要container执行的进程将在这一层执行。

读写层初始是空的,当我们修改文件时,文件会从下面的只读层拷贝到读写层。初始的只读文件依然存在,但被上面的读写层拷贝所隐藏。这就是所谓的copy on write

docker images image列表

docker rmi <image> 删除image

通过Dockfile构建image

Dockfile执行每条命令都相当于:给image添加一层,docker commit提交新image,从新image运行新container

docker build -t <image-name> 根据Dockfile构建image

docker volume

volume是绕过了union mount机制的指定目录,使数据保存功能可以独立于容器。

docker run -v <host-dir>:<container-dir>[:<rw-mode>]-v选项指定volume对应着主机的哪个目录

还有一种用法叫docker volume containerdocker run -v <container-dir>[:<rw-mode>] --name=<volume-container-name>,不用知道也无法知道数据存在主机的哪个目录,其他容器通过docker run --volumes-from=<volume-container-name>使用该volume。即使所有使用该volume的容器都删除了,volume依然存在。

疑:说无法知道数据存在主机的哪个目录,因为用docker inspect <volume-container>查到的对应目录在主机上竟不存在?

docker networking

把多个容器加入同一网络就可通信,默认网络是网桥模式。当容器创建时,会在容器端和主机端创建一对虚拟接口,相当于管道的两端,一端是容器的网络接口,一端插入主机的默认网桥。

同一网络中的容器通过容器名端口号通信。因为docker会自动修改所有容器的/etc/hosts文件,把容器名映射到容器的虚拟内网ip。或者也可在运行docker run -h <host-name> ...时指定主机名。

docker network create <network-name> 创建网络,然后docker run --net=<network-name> ...运行容器或docker network connect <network-name> <container>把已有容器加入网络

docker network inspece <network-name> 查看网络元信息

docker orchestration

orchestration(编排)指管理多个容器,编排工具有docker-composeswarm

docker-compose

docker-compose.yml中配置应用的组件(如images, volumes, links等),然后docker-compose up运行。或着docker-compose up -d来daemon化,然后docker-compose ps查看容器,docker-compose logs查看日志。

swarm

把多个docker主机合成一个对外的虚拟主机

常用docker命令

docker run

docker run -i -t ubuntu /bin/bash 打开终端,-i interactive,-t tty

docker run -d ubuntu /bin/sh -c "..." daemon方式运行,用docker stop <container>停止,docker start <container> 启动,docker attach <container> 再登录

docker run --name=<container-name> ... 给容器命名

docker run -p 8080:80 … 主机端口8080 -> 容器端口80,docker run -d -p 80 ... 主机[32768,61000]随机端口 -> 容器端口80,用docker port <container> <container-port>查看对应的主机端口

docker run -P ... 把容器用到的端口映射到主机的相同端口

docker run --rm ... 运行完就删除的一次性容器

docker ps

docker ps(容器列表:正运行的),docker ps -a(正运行或未运行的),docker ps -l(上次运行的)

docker inspect

docker inspect <container> 查看容器元信息

docker logs

docker logs <container> 查看容器运行输出,docker logs -f <container>类似于tail -f命令

docker top

docker top <container>类似于top命令

参考