Task 1
线性回归
模型
线性回归用于回归预测,即预测一个连续的数值。
一个n元线性回归模型
其中 是要模型学习的值,被称为权重,称为偏置
矩阵形式:
数据集
分为训练集,验证集,测试集。
训练集(平时的作业):用于训练模型以学习真实参数的数据的集合。
验证集(模拟考):通常是从训练集中抽取一部分出来,不参与训练,用于验证训练效果并调整训练超参数的集合。
测试集(高考,考研):以其误差近似模型泛化误差,测试模型的泛化能力。
损失函数
衡量模型参数与真实参数的差距的函数,损失函数越小说明模型参数的分布越逼近真实的参数分布。所以模型学习的目标即为最小化损失函数。
对于线性回归,由于目标是预测一个连续的数值,因此选择平均平方误差和损失函数-均方差损失函数(Mean Square Error-MSE)。
其中常数使对平方项求导后的常数系数为1,这样在形式上稍微简单一些。
为什么要用它作为损失函数?:
在回归问题中,的测量值 会存在一定的误差。
假定对所有的数据点 ,模型预测值 与目标值 之间的误差是一样的,并服从一定的概率分布,比如均值为0,方差为的高斯分布,则有:
[图片上传失败...(image-16013e-1581687516972)]
即:
[图片上传失败...(image-30e8c9-1581687516972)]
对于一组独立同分布的数据点 [图片上传失败...(image-3941f3-1581687516972)] ,以及这些数据对应的目标值 [图片上传失败...(image-4d30cd-1581687516972)] ,我们得到关于这组数据的似然函数:
[图片上传失败...(image-bed5e9-1581687516972)]
其中,高斯分布的概率函数为:
[图片上传失败...(image-ab5ed8-1581687516972)]
可以通过极大化这个似然函数得到关于 的一组极大似然解。
不过,更方便的做法是极大对数似然函数,因为对数函数是严格单增的,所以极大对数似然的解与极大似然的解是相同的。
对数似然函数为:
[图片上传失败...(image-4a09ba-1581687516972)]
如果我们不考虑 的影响,那么,对于参数 来说,最小化平方误差和的解,就等于极大对数似然的估计。
因此,最小化平方误差和 与极大似然等价,考虑到似然函数的定义,优化 相当于在给定高斯误差的假设下,寻找一组 使得观察到目标值的概率最大。
作者:李金ECHO
链接:https://zhuanlan.zhihu.com/p/33568166
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
优化函数-随机梯度下降
用以搜索改进模型参数使得目标(损失函数)最小化的方法。
在每次迭代中,先随机均匀采样一个由固定数目训练数据样本所组成的小批量(mini-batch),然后求小批量中数据样本的平均损失有关模型参数的导数(梯度),最后用此结果与预先设定的一个正数的乘积作为模型参数在本次迭代的减小量。
在上式中, 代表每个小批量中的样本个数(批量大小,batch size), 称作学习率(learning rate)并取正数。
关键如何求出在于损失函数对于所有待学习参数的梯度 -》矩阵求导+反向传播
矩阵求导术(上) - 长躯鬼侠的文章 - 知乎 https://zhuanlan.zhihu.com/p/24709748
矩阵求导术(下) - 长躯鬼侠的文章 - 知乎 https://zhuanlan.zhihu.com/p/24863977
Matrix Cookbook-MIT
其他的一些基于梯度的神经网络优化算法:
- SGD+动量(Momentum)
- Nesterov梯度加速法
- Adagrad
- AdaDelta
- Adam
一文看懂各种神经网络优化算法:从梯度下降到Adam方法 - 量子学园的文章 - 知乎 https://zhuanlan.zhihu.com/p/27449596
矢量计算
在python中,通常使用经过优化的科学计算库如numpy,pytorch等,进行矢量计算比循环快非常多(几十到上百倍)
而且并行化的矢量计算更适于GPU的运算。
pytorch实现以及知识点
import torch
import torch.nn as nn
import torch.utils.data as data
import numpy as np
# 确定真实权重(,偏置)
true_w = [3.5,-1.6]
true_b = 6.6
# 生成若干样本,并在y上加上服从正态分布的误差
X = np.random.rand(10,2)*3
Y = np.dot(X,true_w)
Y+=np.random.normal(0,0.01,10)
X = torch.Tensor(X)
Y = torch.Tensor(Y)
#模型结构
class LinearNet(nn.Module):
def __init__(self):
super(LinearNet,self).__init__()
self.linear = nn.Linear(2,1)
def forward(self,x):
return self.linear(x)
net = LinearNet()
#数据生成器
dataset = data.DataLoader(data.TensorDataset(X,Y),batch_size=2,num_workers=1)
import torch.optim as optim
#声明优化器
optimizer = optim.SGD(net.parameters(), lr=0.03)
epoch = 20
cri = nn.MSELoss()
for i in range(epoch):
for x,y in dataset:
#模型预测结果
out = net(x)
#计算损失
l = cri(out,y.view(-1,1))
#重置优化器梯度
optimizer.zero_grad()
#反向传播
l.backward()
# 一步优化
optimizer.step()
print('epoch %d, loss: %f' % (i+1, l.item()))
Softmax与分类模型
softmax
作用:
将向量的各分量压缩到0-1的范围-概率形式
通过指数函数将最大的分量放大,从而扩大最大的分量与其他分量的差距,同时能保留部分分量(所谓的soft)。
-
与交叉熵损失函数结合,梯度形式简洁
当我们对分类的Loss进行改进的时候,我们要通过梯度下降,每次优化一个step大小的梯度
我们定义选到yi的概率是
[图片上传失败...(image-b9208c-1581687516972)]
然后我们求Loss对每个权重矩阵的偏导,应用链式法则(中间推导省略)。
[图片上传失败...(image-fbee6c-1581687516972)]
最后结果的形式非常的简单,只要将算出来的概率的向量对应的真正结果的那一维减1,就可以了
作者:杨思达zzzz
链接:https://www.zhihu.com/question/23765351/answer/139826397
来源:知乎
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
交叉熵损失函数
衡量数据之间分布差距 -》目标:最小化模型预测分类概率分布与真实分类概率分布的差距
两种理解方式:
-
KL散度
为了让学到的模型分布更贴近真实数据分布,我们最小化 模型数据分布 与 训练数据之间的KL散度,而因为训练数据的分布是固定的,因此最小化KL散度等价于最小化交叉熵。
为什么交叉熵(cross-entropy)可以用于计算代价? - 微调的回答 - 知乎 https://www.zhihu.com/question/65288314/answer/244557337
-
分类模型最大化似然函数
给定一个包含个数据样本的训练集 [图片上传失败...(image-b1664f-1581687516972)] ,以及这些数据对应的类别 [图片上传失败...(image-f69f45-1581687516972)] ,这里, [图片上传失败...(image-8a411b-1581687516972)] ,分类问题的目标是利用这组训练集,寻找一个合适的模型,来预测一个新的数据点 对应的类别 。现在假设模型的参数为 ,模型输出是属于每一类的概率,预测为第 [图片上传失败...(image-dc0399-1581687516972)] 类的概率为 [图片上传失败...(image-22d8e2-1581687516972)] 。
对于样本,其属于第 类的概率为:
[图片上传失败...(image-648599-1581687516972)]
因此,似然函数为:
[图片上传失败...(image-8ed587-1581687516972)]
对数似然为:
[图片上传失败...(image-c0d8b2-1581687516972)]
极大化对数似然,相当于极小化:
[图片上传失败...(image-5fcfe-1581687516972)]
事实上,这正是我们常使用的多类交叉熵损失函数的表示形式。
因此,在分类问题中,最小化交叉熵损失函数相当与极大样本的似然函数。
单层Softmax分类器实现图像分类
import torch
import torchvision
import torchvision.transforms as transforms
import torch.nn as nn
#下载FashionMNIST数据集
mnist_train = torchvision.datasets.FashionMNIST(root='./Datasets/FashionMNIST', train=True, download=True, transform=transforms.ToTensor())
mnist_test = torchvision.datasets.FashionMNIST(root='./Datasets/FashionMNIST', train=False, download=True, transform=transforms.ToTensor())
# 读取数据
batch_size = 256
num_workers = 4
train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, num_workers=num_workers)
test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, num_workers=num_workers)
num_inputs = 784
num_outputs = 10
class classifier(nn.Module):
def __init__(self):
super(classifier,self).__init__()
self.linear = nn.Linear(num_inputs,num_outputs)
self.sm = nn.Softmax(1)
def forward(self,x):
x = self.linear(x.view(-1,num_inputs))
return self.sm(x)
net = classifier()
nn.init.normal_(net.linear.weight, mean=0, std=0.1)
nn.init.constant_(net.linear.bias, val=0)
loss = nn.CrossEntropyLoss()
optimizer = torch.optim.SGD(net.parameters(), lr=0.1)
epoch = 5
for i in range(epoch):
train_l_sum, train_acc_sum, train_num = 0.0, 0.0, 0
for x,y in train_iter:
out = net(x)
l = loss(out,y).sum()
optimizer.zero_grad()
l.backward()
optimizer.step()
train_l_sum += l.item()
train_acc_sum += (out.argmax(dim=1) == y).sum().item()
train_num += y.shape[0]
test_acc_sum,test_num = 0,0
for x,y in test_iter:
out = net(x)
test_acc_sum += (out.argmax(dim=1) == y).sum().item()
test_num += y.shape[0]
print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'
% (i + 1, train_l_sum / train_num, train_acc_sum / train_num, test_acc_sum/test_num))
多层感知机
隐藏层
多层隐藏层可以理解为对数据进行不同层次抽象特征的提取
通用近似定理 (Universal approximation theorem):如果一个前馈神经网络具有线性输出层和至少一层隐藏层,只要给予网络足够数量的神经元,便可以实现以足够高精度来逼近任意一个在 ℝn 的紧子集 (Compact subset) 上的连续函数。
激活函数
仿射变换:
在不增加非线性的激活函数时,多层感知机仍等价于单层仿射变换。
常见激活函数
sigmoid
- 映射到0-1
- 存在梯度消失
- 计算exp消耗较大
tanh
- 存在梯度消失
- 关于原点对称
ReLU
- 运算速度快
- 缓解梯度消失
- 存在神经元死亡现象
LeakyReLU
- 解决神经元死亡问题
PReLU
- 解决神经元死亡问题
Maxout
- 对ReLU和LeakyReLU的一般化归纳
- 解决神经元死亡问题
- 参数数量较多
多层感知机图像分类
将Softmax分类器的结构改为:
num_inputs = 784
num_hidden = 100
num_outputs = 10
class classifier(nn.Module):
def __init__(self):
super(classifier,self).__init__()
self.linear1 = nn.Linear(num_inputs,num_hidden)
self.relu = nn.ReLU()
self.linear2 = nn.Linear(num_hidden,num_outputs)
self.sm = nn.Softmax(1)
def forward(self,x):
x = self.linear1(x.view(-1,num_inputs))
x = self.linear2(self.relu(x))
return self.sm(x)
net = classifier()
nn.init.normal_(net.linear1.weight, mean=0, std=0.1)
nn.init.constant_(net.linear1.bias, val=0)
nn.init.normal_(net.linear2.weight, mean=0, std=0.1)
nn.init.constant_(net.linear2.bias, val=0)
Task 2
文本预处理
读入文本
分词
汉语NLP库:
HanLP
中文分词 词性标注 命名实体识别 依存句法分析 语义依存分析 新词发现 关键词短语提取 自动摘要 文本分类聚类 拼音简繁转换 自然语言处理
https://github.com/hankcs/HanLP
jieba
结巴中文分词
https://github.com/fxsjy/jieba
综合
awesome-chinese-nlp 中文自然语言处理相关资料
https://github.com/crownpku/Awesome-Chinese-NLP
其他语言NLP库:
综合
awesome-nlp - A curated list of resources dedicated to Natural Language Processing (NLP)
https://github.com/keon/awesome-nlp
NLTK
建立字典
考虑词频阈值,是否使用特殊token
- '<pad>' - padding, 填充句子使得句子等长
- '<bos>' - beginning of sentence,句子开头标记
- '<eos>' - end of sentence, 句子结束标记
- '<unk>' - unknown,不在词库中的词的标记
词序列转换为索引序列
语言模型
基于n元语法的语言模型
- 语言模型 - 用于计算一个句子是人讲的概率的模型(通顺与否)
词语序列
则一个句子的概率表示为
- 马尔科夫假设 - 任意一个词出现的概率之和它前面的几个词(上文)有关
一元模型:
二元模型:
时序数据的采样
随机采样
每个样本是原始序列上任意截取的一段序列,相邻的两个随机小批量在原始序列上的位置不一定相毗
X: tensor([[ 6, 7, 8, 9, 10, 11],
[12, 13, 14, 15, 16, 17]])
Y: tensor([[ 7, 8, 9, 10, 11, 12],
[13, 14, 15, 16, 17, 18]])
X: tensor([[ 0, 1, 2, 3, 4, 5],
[18, 19, 20, 21, 22, 23]])
Y: tensor([[ 1, 2, 3, 4, 5, 6],
[19, 20, 21, 22, 23, 24]])
相邻采样
在相邻采样中,相邻的两个随机小批量在原始序列上的位置相毗邻。
X: tensor([[ 0, 1, 2, 3, 4, 5],
[15, 16, 17, 18, 19, 20]])
Y: tensor([[ 1, 2, 3, 4, 5, 6],
[16, 17, 18, 19, 20, 21]])
X: tensor([[ 6, 7, 8, 9, 10, 11],
[21, 22, 23, 24, 25, 26]])
Y: tensor([[ 7, 8, 9, 10, 11, 12],
[22, 23, 24, 25, 26, 27]])
循环神经网络
[图片上传失败...(image-f8e810-1581687516972)]
在时间步t,隐藏状态:
输出为:(没有激活函数)
隐藏状态可视为保存了在该时间步前的历史信息
Task 3
过拟合、欠拟合及其解决方案
- 训练误差 - 模型在训练集上表现得误差
- 泛化误差 - 模型在任意非训练集样本数表现得误差的(期望)
欠拟合
模型的训练误差和泛化误差都较高,即模型不能很好地从训练样本中学习
解决方法:
增加输入特征
减少正则化参数
增加模型复杂度
过拟合
模型的训练误差远低于泛化误差,即模型学习到的特征不能很好地从训练集泛化到测试集,而是只属于训练集的特征
解决办法:
增加训练样本
采用正则化方法
dropout-丢弃法
Batch Normalization
梯度消失与梯度爆炸
梯度消失与梯度爆炸的根本原因都是由于梯度更新的反向传播方法,在神经网络层数过深时,对于离输出层越远的层,如果输出层的梯度大于1,其梯度会呈指数形式增加;如果小于1,其梯度会呈指数形式递减。
梯度消失一般出现于深层网络,或使用了不合适的激活函数
梯度爆炸一般出现于深层网络,或权值初始化太大
解决方法:
- - 预训练加微调
- - 梯度剪切、权重正则(衰减)(针对梯度爆炸)
- - 使用不同的激活函数
- - 使用batchnorm
- - 使用残差结构
- - 使用LSTM网络
详解深度学习中的梯度消失、爆炸原因及其解决方法 - DoubleV的文章 - 知乎 https://zhuanlan.zhihu.com/p/33006526
循环神经网络进阶
GRU - Gate Recurrent Unit
[图片上传失败...(image-f03716-1581687516972)]
[图片上传失败...(image-e0154-1581687516972)]
Reset door 重置门
Update door 更新门
候选隐藏状态门
原文中隐藏状态与输入的连结方式是 Concatenate,此处公式等价
在更新门中或在候选状态中等价
LSTM - Long Short Term Memory
[图片上传失败...(image-c908d6-1581687516972)]
人人都能看懂的LSTM介绍及反向传播算法推导(非常详细) - 陈楠的文章 - 知乎 https://zhuanlan.zhihu.com/p/83496936
LSTM细节分析理解(pytorch版) - ymmy的文章 - 知乎 https://zhuanlan.zhihu.com/p/79064602