简单的回归问题
加入噪声后:
~
loss=——这是一个矩阵形式
Minimize loss
w'*x+b'y
例:
points是一系列的x和y的组合,对每一个点进行循环迭代。然后去每个点的x和y值,用wx+b预测的值和实际值y相加求出平方和。最后对totalError求一个平均。
接着计算梯度信息。由loss function,然后分别对W和b进行求导。因为代码中将导数进行了累加,所以我们要进行average,除以N
对梯度进行迭代。num_iterations为迭代次数。迭代完之后,将最后一组的w,b返回,就是我们要求的最优解
这是用numpy生成的100组x和y(这里只截了一部分)
这是总的函数,先把points用numpy导入进来,然后用gradient_descent_runner跑100个epoch
最后的结果可以看出b和w与真实的值很接近,说明拟合的很好
手写数字体——MNIST
0-9十个数字,对应10个类别,每一类有7000张照片,一共有70k张。如果全部用于训练,则会导致过拟合。即可能只是记住了这70k张照片,但是对于新的数据集来说,识别效果并不好。所以我们将70k张照片,60k用于训练,10k用于测试。
因此将矩阵打平
运用了三层网络迭代的方式,H3做为输出层。又由于如果传统的123来进行编码的话,会存在1<2<3的大小关系,而在标签分类中,不同类之间并没有什么相关性,所以引入one-hot编码的方式贴标签。
loss function的计算,引入欧氏距离,用预测值与真实值相减,求平方。这种方法对于手写数字体识别来说,效果可能并不太好,因为字体可能倾斜或者扭曲,这些非线性因素必须考虑,所以引入非线性参数(Non-Linear Factor)
这里的ReLU函数,可以看到,随着输入的变化,输出变化也相同。
接下来用梯度下降,找到一组w,b,使得对于任意的x,都有最小,这就是loss function最小
max与argmax(pred)区别:max=0.8,则armax(pred)=1(对于概率最大的标签号)。
首先,导入我们需要的工具包。
nn 用于神经网络的相关工作
optim 用于优化的一个工具包
torchvision 为了可视化的一个工具包
utils 里面有着几个绘图函数,plot_curve——绘制下降曲线,plot_image——绘制图片,显示6张图片,按照两行三列摆放。one_hot,用one_hot的编码方式贴标签。如果一个标签直接是索引并不是一维张量的话,就可以调用这个函数,先用zero初始化好这个矩阵,然后在指定的维度上面根据索引填充1就好了,那必然未被填充到的肯定是默认值,就是为0。
加载数据集
加载数据集,给一个mnist_data的路径。这里的数据用于训练,数据从网上下载下来。接着读取数据时,讲numpy数据类型转化成tensor,作为pytorch的一个数据载体,并进行正则化,使得让数据在0的附近均匀分布。同理,后半段数据作为测试数据,进行相同的数据类型转换。
batch_size表示一次性加载的图片数量,shuffle表示是否对图片进行打散
对训练的数据进行迭代,被next函数不断调用返回下一个值。
输出结果范围在0的左右附近,总共读取了512张图片
这是训练匹配的结果。一般都会是正确匹配的。
创建网络
首先初始化网络,一共有三层网络,用nn.Linear函数创建。28*28和10作为第一层输入和最后一次输出是由题目确定的,其他数据都是自己随意确定的。
接着创建计算的过程 def forward(self,x):
x:[b,1,28,28]
其中对每层加入非线性单元F.relu()。
训练阶段
首先,我们对数据集会迭代3次,然后里面那层,相当于对一个batch(512张图片)迭代1遍,可以看到结果x和y分别是512张图片 x:[b, 1, 28, 28], y: [512]
接着进行网络的初始化
为了得到输出,将输入x通过这个网络
这是一个全连接神经网络,它只接受2维的tensor,而我们的x是4维的,所以在通过网络之前,先要做一个mapping。
其中x.size(0)是一个batch512,就是将x映射成一个(b,784)的矩阵。然后通过net,将x映射成=> [b, 10]
然后将真实的y,通过one-hot转化为[b,10],这样和x映射结果都是[b,10]。我们希望out和y_onehot越接近越好。
所用我们用.mse_loss来计算out和y_onehot之间的均方差,得到 loss。
然后通过loss.backward()函数计算loss的梯度。
为了能够更新梯度,我们使用了optimizer的优化器,net.parameters()返回的是[w1, b1, w2, b2, w3, b3],同时我们还要设置学习率lr=0.01,以及动量momentum=0.9,便于更好的优化。
完整的过程就是,先对梯度进行清零,然后计算梯度,最后更新梯度。然后不停的循环,最终循环结束后,会得到一个较好的[w1, b1, w2, b2, w3, b3]。
epoch表示训练总共有60k张图片,一个batch_size是512。而batch_idx表示batch的缩索引。batch_idx=epoch/batch_size,大约是117次。而这里每10个打印一次,所以一轮打印大约12次。
从结果看,经过对一个batch循环一次后,在对epoch循环三次后,loss明显得到了下降。
为了可视化,将loss数值取出,通过plot_curve绘制成图像。由于loss本身是tensor类型,而为了取出将其转化成numpy类型。
可视化之后,得到了下降误差下降曲线。
测试阶段
进入测试阶段,前半部分和训练阶段类似,将输入x做一个mapping,将它通过net网络输出。即将4维的数据转换成2维的[b,10]。预测值pred就是对应的正确的标签。
CNN+mnist手写数字体识别
引入库函数
预设超参数
加载数据集
这里我们使用dataloader迭代器来加载数据集(迭代器的作用可以减少内存的占用)
将mnist训练集和测试集下载到文件夹data中对数据进行变化,包括转为tensor数据类型,以及为了保证训练集测试集的独立同分布,数据规范化到正态分布;数据分批量存储,顺序打乱,方便后续训练。
设计CNN网络
一个简单的卷积神经网络的结构,一般包括:
卷积层Conv:通过卷积核提取图像特征,得到feature map
池化层Pool:利用卷积核对feature map 降采样,减少尺寸。最大池化层就是取滑动窗口内最大的像素,而平均池化层就是取滑动窗口内平均像素结果。
全连接层:多个linear model + 激活函数 eg:ReLU。
这样就构成了一个基本的CNN了,但是,为了提高模型的泛化能力,进行了:
batch normalization:简单来说就是将上一层的加权求和的所有输出结果再批量归一化(标准正态分布),然后输入一个线性模型,然后再连接到激活函数。
DropOut:在全连接层中,我们通过设定概率随机的让这一层的某些权重为0,相当于神经元无效。
训练前准备
定义模型优化器:输入模型参数,定义初始学习率
定义学习率调度器:输入包装的模型,定义学习率衰减周期step_size,gamma为乘法的衰减因子
在官网上的解释。如果初始学习率lr = 0.05,衰减周期step_size为30,衰减乘法因子gamma=0.01# Assuming optimizer uses lr = 0.05 for all groups
# >>> # lr = 0.05 if epoch < 30
# >>> # lr = 0.005 if 30 <= epoch < 60
# >>> # lr = 0.0005 if 60 <= epoch < 90
训练模块
把训练模块封装起来,便于多次调用
_model.train()——将模式调成训练模式
_lr_scheduler.step()——设置学习率调度器准备开始更新
预测模块
运行
结果
这里只跑了两个epoch,训练达到一定次数,泛化能力会增强,但是训练过度后,泛化能力会下降(过拟合)。所以,要合理制定epoch。