一篇文章教你掌握——Pytorch深度学习实践
- 1. Overview 概述
- 1.1 Rule-based systems 基于规则的系统
- 1.2 Classic machine learning 经典机器学习
- 1.3 Representation learning 表征学习
- 1.4 Brief history of neural networks 神经网络简史
- 2. 配置环境
- 2.1 安装Anaconda
- 2.2 创建虚拟环境
- 2.3 确认CUDA
- 2.4 利用conda或者pip安装PyTorch
- 2.5 安装CUDA
- 2.6 安装PyTorch
- 2.7 验证pytorch是否安装成功
- 3. 线性模型 Linear Model
- 3.1 在机器学习上的概念
- 3.2 模型设计
- 3.3 模型实现
- 4. 梯度下降算法 Gradient Descent
- 4.1 概念
- 4.2 梯度下降设计
- 4.3 梯度下降代码实现
- 4.4 随机梯度下降设计
- 4.5 随机梯度下降代码
- 5. 反向传播 Back Propagation
- 5.1 概念
- 5.2 设计
- 5.3 代码实现
- 6. 使用 PyTorch 进行线性回归 Linear Regression with PyTorch
- 6.1 设计
- 6.2 代码实现
- 6.3 补充要点
- 7. 逻辑回归 Logistic Regression
- 7.1 概念
- 7.2 设计
- 7.3 代码实现
- 8. 多维输入 Multiple Dimension Input
- 8.1 概念
- 8.2 设计
- 8.3 代码实现
- 8.4 补充要点
- 9. 数据集和数据加载器 Dataset and DataLoader
- 9.1 概念
- 9.2 设计
- 9.3 代码实现
- 10. 多分类问题 Softmax分类器 Softmax Classifier
- 10.1 概念
- 10.2 设计
- 10.3 代码实现
- 10.4 补充要点
前言:
首先感谢大家的支持,感谢新老粉及本人达成50W+的阅读量,这篇文章花费了大量的心血,后继也会继续更新CNN、RNN等神经网络的文章,本人也将会继续努力创作更好的文章,数据分析的文章已经在继续更新了,希望大家多多支持,一起学习共同进步,赢在数字化时代!
1. Overview 概述
Infer 推理;Prediction 预测
1.1 Rule-based systems 基于规则的系统
1.2 Classic machine learning 经典机器学习
是否大于50个样本——是分类问题——有标签是分类器/没标签用聚类
是否大于50个样本——不是分类问题——查看预测量级——预测数值用回归/用降维
1.3 Representation learning 表征学习
表征学习出现的原因:
Features 特征;Mapping from features 从特征映射;Additional layers of more abstract features 更多抽象特征的附加层
1.4 Brief history of neural networks 神经网络简史
Perceptron 感知器 Artificial Neural Network 人工神经网络
Back Propagation 反向传播 (偏导数)
各个神经网络架构:
2. 配置环境
2.1 安装Anaconda
官网直接安装,并且配置下path
2.2 创建虚拟环境
conda env list 查看虚拟环境 (*代表在哪个环境下)
conda create -n 环境名字 python=版本
(conda create -n 环境名字 python=版本 -c 镜像地址)
我查看了pytorch官网目前显示(Latest PyTorch requires Python 3.8 or later. For more details, see Python section below.)我们至少要装3.8以上的版本,我们装3.9
确认下载依赖功能包:
conda activate yixuepytorch 进入我们创建好的虚拟环境
conda list 查看当下环境下,有哪些功能包
conda remove -n 虚拟环境名字 --all 删除所选环境
命令总结:
conda env list # 查看虚拟环境
conda create -n 环境名字 python=版本 # 创建新环境
#(conda create -n 环境名字 python=版本 -c 镜像地址)
conda activate yixuepytorch # 进入我们创建好的虚拟环境
conda list # 查看当下环境下,有哪些功能包
conda remove -n 虚拟环境名字 --all # 删除所选环境
2.3 确认CUDA
- 首先确定自己显卡的算力-确定自己显卡型号(通过任务管理器可以看到)
- 确定自己的可选择的CUDA Runtime Version
- 确保自己的CUDA Driver 版本 >= CUDA Runtime 版本
我的显卡是:
算力可以上维基百科或者google上查,我的GeForce RTX 4050, 8.9
那么就可以确定 CUDA Runtime
查看CUDA Driver 版本:
在命令行输入 nvidia-smi
我的是12.3版本
最终确认我们适用的CUDA版本为11.8-12.3
2.4 利用conda或者pip安装PyTorch
命令总结:
conda install xxx #(conda install xxx -c 通道地址)
conda create -n yyy #(conda create -n yyy -c 通道地址)
conda config --show # 查看conda配置文件
conda config --get # 得到有哪些通道
conda config --add channels 通道地址 # 持久化添加通道地址
conda config --remove channels 通道地址# 持久化删除通道地址
(channel 通道,就是下载地址;defaults指的是官方地址)
2.5 安装CUDA
- 安装显卡驱动
上英伟达官网,选择驱动,根据我们从任务管理器选择的显卡型号选择驱动(其中notebook意思是笔记本),比如我的是:
安装后我的变成了12.4更新为了最新版本
2.6 安装PyTorch
官网确定CUDA Runtime版本我确定了12.1
可以从官网确定命令安装
也可以通过添加镜像源
先进入我们的虚拟环境
然后复制命令安装,查看是否正确
conda list 查看下功能包里是否有了pytorch
对于历史版本的pytorch在这里:
2.7 验证pytorch是否安装成功
- 进入对应的虚拟环境
- conda list 查看是否有没有pytorch或者torch
- 输入python 进入python环境中
- 输入import torch
- 输入torch.cuda.is_available() #pytorch验证是否使用电脑的gpu
- 显示true则安装成功
- print(torch.__version __) # 显示pytorch版本
3. 线性模型 Linear Model
在我们做科研的步骤当中,遵循:
- 准备数据集 DataSet
- 准备模型选择或者说模型设计 Model
- 训练 Training
- 推理 Inferring
3.1 在机器学习上的概念
那么线性模型的样子大概就为:
Prediction 预测
训练可以看到输入和输出;测试只能看到输入,根据训练出来的函数去匹配测试是否合适;
模型不要过拟合;要有好的泛化能力
训练再拆为两份,一份训练一份开发集做评估;然后再在测试集看是否好;
3.2 模型设计
我们接下来的任务是w和b的值到底是多少;预测结果我们一般叫y_hat
机器是去随机猜测数值,并且查看与实际数值的误差求最小(也叫Compute Loss计算损失):
由于在点的下面,差值为负,我们作为求平方
我们在不断求w的时候,找到mean最小的时候的值
Loss function & Cost function 接着设计损失函数
即得出:
3.3 模型实现
# 导入功能包
import numpy as np
import matplotlib.pyplot as plt
# 准备训练集
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
# 定义模型,也就是前面说的Linear Model,也就是前馈计算
def forward(x):
return x * w
# 定义损失函数,也就是前面说的Loss Function
def loss(x, y):
y_pred = forward(x)
return (y_pred - y) * (y_pred - y)
# 创建空列表来保存 权重和权重的损失值(mse指的是损失)
w_list = []
mse_list = []
# 设置采样间隔
for w in np.arange(0.0, 4.1, 0.1):
print('w=', w)
l_sum = 0
# x和y的val值不断从数据中取
for x_val, y_val in zip(x_data, y_data):
y_pred_val = forward(x_val) # 计算预测值
loss_val = loss(x_val, y_val) # 计算损失值
l_sum += loss_val # 计算损失求和
print('\t', x_val, y_val, y_pred_val, loss_val)
print('MSE=', l_sum / 3)
# 保存进空列表
w_list.append(w)
mse_list.append(l_sum / 3)
# 画图显示
plt.plot(w_list, mse_list)
plt.ylabel('Loss')
plt.xlabel('w')
plt.show()
我们将来不会拿权重画图,但是看超参数时,我们可以用这种图进行判别。(一般横坐标为epoch)
利用visdom功能包可以做深度学习的可视化,长时间的深度学习要学会存盘。
对于画三维的图功能包使用说明文档:
The mplot3d toolkit和numpy.meshgrid
解决y=wx+b的线性模型
# 导入功能包
import numpy as np
import matplotlib.pyplot as plt
from mpl_toolkits.mplot3d import Axes3D
# 这里设函数为y=3x+2
x_data = [1.0,2.0,3.0]
y_data = [5.0,8.0,11.0]
# 定义模型,也就是前面说的Linear Model
def forward(x):
return x * w + b
# 定义损失函数,也就是前面说的Loss Function
def loss(x,y):
y_pred = forward(x)
return (y_pred-y)*(y_pred-y)
# 创建空列表来保存 权重的损失值
mse_list = []
# 设置采样间隔
W = np.arange(0.0,4.1,0.1)
B = np.arange(0.0,4.1,0.1)
[w,b] = np.meshgrid(W,B)
l_sum = 0
for x_val, y_val in zip(x_data, y_data):
y_pred_val = forward(x_val)
print(y_pred_val)
loss_val = loss(x_val, y_val)
l_sum += loss_val
# 画图
fig = plt.figure()
ax = Axes3D(fig)
fig.add_axes(ax) # python3.8以上版本需要添加此项操作才能画出3D图
ax.plot_surface(w, b, l_sum/3)
plt.show()
更多内容线性回归模型可以看我写的:Python大数据分析——一元与多元线性回归模型
4. 梯度下降算法 Gradient Descent
但是搜索量,参数越多,穷举法是不可能的
4.1 概念
所以我们要改良方法,可以用分治法,但是分治法有一点缺陷是进入局部最优解
什么是局部最优解:
那么我们需要解决的问题就是:Optimization Problem 最优化问题
这就我们介绍下梯度下降算法Gradient Descent Algorithm
Gradient 梯度;这里的x指的是权重w;导数为负,说明为递减方向,也就是我们找导数负的
4.2 梯度下降设计
所以我们在梯度下降算法中(类似于贪心),更新权重的方法(其中α是学习率,一般要取小一点):
梯度下降不一定能得到最优结果,但能得到局部最优结果,因为他可能是非凸函数:
还有一种可能是死于鞍点,没法继续迭代,什么是鞍点:
这个东西是我们之后设计要考虑的,我们这节主要搞清楚什么是梯度下降,利用我们第三节的线性模型来讲解
首先算清楚偏微分是怎样的:
然后套入我们的损失函数当中:
4.3 梯度下降代码实现
Epoch 叫迭代轮数
# 导入功能包
import matplotlib.pyplot as plt
# 建立数据集
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
# 设定初始权重(猜测),和学习率
w = 1.0
alpha = 0.01
# 定义模型,也就是前馈计算
def forward(x):
return x * w
# 定义成本函数
def cost(xs, ys):
cost = 0 # 定义初始值
for x, y in zip(xs, ys): # 循环;zip就是把列表对应第i个元素组成一个新的列表
y_pred = forward(x) # 函数
cost += (y_pred - y) ** 2 # 对损失值求和
return cost / len(xs) # 除以样本的数量,也就是1/N
# 定义梯度函数
def gradient(xs, ys):
grad = 0 # 定义初始值
for x, y in zip(xs, ys): # 循环;zip就是把列表对应第i个元素组成一个新的列表
grad += 2 * x * (x * w - y) # 求和
return grad / len(xs)
# 打印下进行个分割
print('Predict (before training)', 4, forward(4))
# 定义空列表用来画图
epoch_lst = []
cost_lst = []
# 训练过程,每次拿权重-学习率*梯度
for epoch in range(100): # 设置了100次训练
cost_val = cost(x_data, y_data)
grad_val = gradient(x_data, y_data)
w -= alpha * grad_val
cost_lst.append(cost_val) # 用来画图做记录
epoch_lst.append(epoch) # 用来画图做记录
print('Epoch:', epoch, 'w=', w, 'loss=', cost_val) # 打印查看信息,几轮,当前权重,损失函数多少
# 打印下训练之后
print('Predict (after training)', 4, forward(4))
# 画图
plt.plot(epoch_lst,cost_lst)
plt.ylabel('cost')
plt.xlabel('cost')
plt.show()
注意训练的结果一定是收敛的,如果说发散了,说明学习率大了,降低一点学习率
4.4 随机梯度下降设计
随机梯度下降 Stochastic Gradient Descent;损失函数的导数 Derivative of Loss Function
cost是所有样本,随机是N个数据里选一个;我们的数据是有噪声的,可能会把我们做推动
4.5 随机梯度下降代码
# 建立数据集
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
# 设定初始权重(猜测),和学习率
w = 1.0
alpha = 0.01
# 定义模型,也就是前馈计算
def forward(x):
return x * w
# 定义损失函数
def loss(x, y):
y_pred = forward(x)
return (y_pred - y) ** 2
# 定义梯度函数
def gradient(x, y):
return 2 * x * (x * w - y)
# 打印下进行个分割
print('Predict (before training)', 4, forward(4))
# 按训练集样本的每个梯度更新权重
for epoch in range(100):
for x, y in zip(x_data, y_data):
grad = gradient(x, y) # 对每一个样本来求梯度
w = w - alpha * grad
print("\tgrad: ", x, y, grad)
l = loss(x, y)
print("progress:", epoch, "w=", w, "loss=", l)
# 打印下训练之后
print('Predict (after training)', 4, forward(4))
这就是表示,一起算和一个个去算(在性能与时间复杂度取一个折中,叫做batch/mini-batch,批量的随机梯度下降)
5. 反向传播 Back Propagation
在上节中我们的模型,可以看作一个非常简单的神经网络
5.1 概念
Neuron 神经元;Stochastic Gradient Descent 随机梯度下降;Derivative of Loss Function 损失函数的导数;
简单模型可以通过解析式做,但是复杂网络就不能了,复杂网络:
对于5个输入x,中间隐层H(1)对应的是6个元素,那w权重就有6*5=30个
那么我们的计算图Computational Graph:
MM 矩阵乘法(缩写);ADD 向量加法
对于矩阵的求导计算,可以看这本书(对不同的求导,对应的梯度怎么计算):matrix cookbook
我们展开来观察一下:
发现是无论是几层,形式是一样的;为了使其有意义,我们要加一个非线性的变化函数,这样就没法展开了
Nonlinear Function 非线性函数
The composition of functions and Chain Rule 函数的组合和链式法则;进行累计,整体导数就求出来了
5.2 设计
- Chain Rule – 1. Create Computational Graph (Forward) 链式法则 – 1. 创建计算图(正向)
- Local Gradient 局部梯度
- Given gradient from successive node 给定连续节点的梯度
- Use chain rule to compute the gradient (Backward) 使用链式法则计算梯度(向后)
举个例子,f=xw:
我们来看下完整的序列图,前馈加反馈(其中下面的导数是求局部梯度)
对于y=xw+b
5.3 代码实现
在 PyTorch 中,Tensor 是构建动态计算图的重要组成部分。储存w和损失函数对权重的导数等
它包含 data 和 grad,分别存储节点值和梯度 w.r.t 损失。
接下来我们就要用gpu pytorch了,你可以在命令行直接进入环境,然后进入python编程,但是这样非常费劲,如果我们想要在jupyter notebook里进行调用pytorch就要进去对应的环境(当然如果pytorch装入了base环境中就可以直接调用了)
# 首先进入对应的环境
conda activate pytorch
# 安装一个配置功能包
conda install ipykernel
# 创建对应notebook环境调用名称
python -m ipykernel install --name yixuepytorch
然后我们创建新的notebook的时候就能选用其他环境了
# 导入功能包
import torch
# 创建数据样本
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
# 选择权重(如果需要autograd机制,Tensor的元素变量requires_grad必须设置为True)
w = torch.Tensor([1.0]) # 这里w只有一个初始值
w.requires_grad = True # 设置需要计算梯度
# 设置学习率
alpha = 0.01
# 定义模型,也就是前馈计算
def forward(x):
return x * w # 这里面的w是一个Tensor
# 定义损失函数
def loss(x, y):
y_pred = forward(x)
return (y_pred - y) ** 2
# 打印下进行个分割
print('Predict (before training)', 4, forward(4))
# 训练过程
for epoch in range(100): # 设置了100次训练
for x, y in zip(x_data, y_data): # # zip就是把列表对应第i个元素组成一个新的列表
l = loss(x, y) # 前馈过程,计算loss(为张量)
l.backward() # 反馈过程(向后,计算 require_grad 设置为 True 的 Tensor 的 grad,求梯度)/ 其中计算图也被释放了
print('\tgrad:', x, y, w.grad.item())
w.data = w.data - alpha * w.grad.data # 利用梯度来更新权重,data指的是数值,权重更新的时候对的是值的操作而不是张量tensor
w.grad.data.zero_() # .backward() 计算的梯度将被累加,所以更新后要记得把权重数据里的梯度清零
print("progress:", epoch, l.item()) # 输出训练论数、最后的loss
# 其中.data是进tensor修改;。item是把其中的数取出来
# 打印下训练之后
print("predict (after training)", 4, forward(4).item())
梯度也是tensor
再来个例子:
# 导入功能包
import torch
# 创建数据样本
x_data = [1.0, 2.0, 3.0]
y_data = [2.0, 4.0, 6.0]
# 选择权重(如果需要autograd机制,Tensor的元素变量requires_grad必须设置为True)
w1 = torch.Tensor([1.0]) # 初始权值
w1.requires_grad = True # 计算梯度,默认是不计算的
w2 = torch.Tensor([1.0])
w2.requires_grad = True
b = torch.Tensor([1.0])
b.requires_grad = True
# 设置学习率
alpha = 0.01
# 定义模型,也就是前馈计算
def forward(x):
return w1 * x**2 + w2 * x + b # 这里面的w是一个Tensor
# 定义损失函数
def loss(x, y):
y_pred = forward(x)
return (y_pred - y) ** 2
# 打印下进行个分割
print('Predict (before training)', 4, forward(4))
# 训练过程
for epoch in range(100): # 设置了100次训练
l = loss(1, 2) #为了在for循环之前定义l,以便之后的输出,无实际意义
for x,y in zip(x_data,y_data): # zip就是把列表对应第i个元素组成一个新的列表
l = loss(x, y) # 前馈过程,计算loss(为张量)
l.backward() # 反馈过程(向后,计算 require_grad 设置为 True 的 Tensor 的 grad,求梯度)/ 其中计算图也被释放了
print('\tgrad:',x,y,w1.grad.item(),w2.grad.item(),b.grad.item())
w1.data = w1.data - 0.01 * w1.grad.data # 利用梯度来更新权重,注意这里的grad是一个tensor,所以要取他的data
w2.data = w2.data - 0.01 * w2.grad.data
b.data = b.data - 0.01 * b.grad.data
w1.grad.data.zero_() # 释放之前计算的梯度
w2.grad.data.zero_()
b.grad.data.zero_()
print('Epoch:',epoch,l.item()) # 输出训练论数、最后的loss
# 打印下训练之后
print("predict (after training)", 4, forward(4).item())
在用y=w1x²+w2x+b的模型训练100次后可以看到当x=4时,y=8.5,与正确值8相差比较大。原因可能是数据集本身是一次函数的数据,模型是二次函数。所以模型本身就不适合这个数据集,所以才导致预测结果和正确值相差比较大的情况。
6. 使用 PyTorch 进行线性回归 Linear Regression with PyTorch
这节主要是如何用pytorch更方便的实现之前的模型、梯度下降反向传播这些。
步骤:
1、准备数据集
2、使用类设计模型
3、构造损失和优化器
4、训练周期(前馈、反馈和更新)
6.1 设计
在 PyTorch 中,计算图采用小批量方式,因此 X 和 Y 是 3 × 1 张量。
- 准备数据集
- 设计模型(由求导变为构造好计算图,自动求导)
!!!首先将模型构建为一个类:
我们的模型类应该继承自 nn.Module,它是所有神经网络模块的基类
nn.Linear 类实现了神奇的方法 __call __(),它使得类的实例可以像函数一样被调用。 通常会调用forward()。
# 创建模型类
class LinearModel(torch.nn.Module):
def __init__(self): # 构造函数,做初始化对象默认调用的函数
super(LinearModel, self).__init__() # 调用父类构造
self.linear = torch.nn.Linear(1, 1) # nn.Linear 类包含两个张量(对象):权重和偏差。
def forward(self, x): # 前馈计算
y_pred = self.linear(x) # 定义可调用(nn.Linear类实现了神奇的方法 __call__(),它使得类的实例可以像函数一样被调用。)
return y_pred
model = LinearModel() # 创建 LinearModel 类的实例。为callable,可以直接调用,比如model(x)
- 构造损失函数和优化器
MSELoss是
优化器是告诉哪的tensor做优化,告诉优化器哪些参数需要进行随机梯度下降(实现随机梯度下降(可选的动量)),parameters这个参数不管模型多复杂,都能找到所有的参数;lr是学习率
- 训练过程
.backward()计算出的梯度将被累加。
所以在倒退之前,记住将梯度设置为零!
# 训练100次
for epoch in range(100):
y_pred = model(x_data) # 前馈过程算y预测
loss = criterion(y_pred, y_data) # 算损失函数
print(epoch, loss)
optimizer.zero_grad() # 梯度清理0
loss.backward() # 反向传播
optimizer.step() # 更新所有权重(参数)
# 输出权重和偏差
print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())
# 测试模型
x_test = torch.Tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)
100次才7.4说明我们收敛的还不够好,训练1000次发现越来越好
注意除了观察训练集上的收敛,也要观察测试集上的好坏, 防止过拟合
6.2 代码实现
# 导入功能包
import torch
# 导入数据集
x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[2.0], [4.0], [6.0]])
# 创建模型类
class LinearModel(torch.nn.Module):
def __init__(self): # 构造函数,做初始化对象默认调用的函数
super(LinearModel, self).__init__() # 调用父类构造
self.linear = torch.nn.Linear(1, 1) # nn.Linear 类包含两个张量(对象):权重和偏差。
def forward(self, x): # 前馈计算
y_pred = self.linear(x) # 定义可调用(nn.Linear类实现了神奇的方法 __call__(),它使得类的实例可以像函数一样被调用。)
return y_pred
model = LinearModel() # 创建 LinearModel 类的实例。为callable,可以直接调用,比如model(x)
criterion = torch.nn.MSELoss(size_average=False) # 构造损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 构造优化器,实现随机梯度下降(可选的动量)
# 训练100次
for epoch in range(100):
y_pred = model(x_data) # 前馈过程算y预测
loss = criterion(y_pred, y_data) # 算损失函数
print(epoch, loss)
optimizer.zero_grad() # 梯度清理0
loss.backward() # 反向传播
optimizer.step() # 更新所有权重(参数)
# 输出权重和偏差
print('w = ', model.linear.weight.item())
print('b = ', model.linear.bias.item())
# 测试模型
x_test = torch.Tensor([[4.0]])
y_test = model(x_test)
print('y_pred = ', y_test.data)
6.3 补充要点
class的使用
更多的优化器:
• torch.optim.Adagrad • torch.optim.Adam • torch.optim.Adamax • torch.optim.ASGD • torch.optim.LBFGS • torch.optim.RMSprop • torch.optim.Rprop • torch.optim.SGD
更多的pytorch可以看官方教程
7. 逻辑回归 Logistic Regression
7.1 概念
虽然叫回归,但做的是分类问题;现实生活中也基本上很多问题需要解决的也是分类问题。回归是连续的,分类是离散的(分类问题算的是样本属于所有类别的概率值;在分类中,模型的输出是输入属于确切类别的概率。)
比较一下问题的解决:
logistic函数由实数空间映射到0-1之间
经典的两个练习有:
7.2 设计
sigmoid函数
但在pytorch里他是如概念里的图
比较下我们之前的,他的设计图:
损失函数的改变(之前是mse,mse是计算两个实数之间的差值):
两个分布之间差异性的大小(交叉熵):
二元分类的小批量损失函数
7.3 代码实现
因为在σ操作里他是没有参数的,所以不需要在构造函数中初始化它
# 导入功能包
import torch.nn.functional as F
# 创建模型类
class LogisticRegressionModel(torch.nn.Module):
def __init__(self): # 构造函数,做初始化对象默认调用的函数
super(LogisticRegressionModel, self).__init__() # 调用父类构造
self.linear = torch.nn.Linear(1, 1) # nn.Linear 类包含两个张量(对象):权重和偏差。
def forward(self, x): # 前馈计算
y_pred = F.sigmoid(self.linear(x)) # 可调用函数,求完前面线性的值再应用到sigmoid里当中
损失函数:
criterion = torch.nn.BCELoss(size_average=False) # BCELoss就是cross-entropy(交叉熵)
# 导入功能包
import torch
import torch.nn.functional as F
# 数据集
x_data = torch.Tensor([[1.0], [2.0], [3.0]])
y_data = torch.Tensor([[0], [0], [1]]) # 0和1类(二分类)
# 创建模型类
class LogisticRegressionModel(torch.nn.Module):
def __init__(self): # 构造函数,做初始化对象默认调用的函数
super(LogisticRegressionModel, self).__init__() # 调用父类构造
self.linear = torch.nn.Linear(1, 1) # nn.Linear 类包含两个张量(对象):权重和偏差。
def forward(self, x): # 前馈计算
y_pred = F.sigmoid(self.linear(x)) # 可调用函数,求完前面线性的值再应用到sigmoid里当中
return y_pred
model = LogisticRegressionModel()
criterion = torch.nn.BCELoss(size_average=False) # 构造损失函数
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 构造优化器,实现随机梯度下降(可选的动量)
# 训练1000次
for epoch in range(1000):
y_pred = model(x_data) # 前馈过程算y预测
loss = criterion(y_pred, y_data) # 算损失函数
print(epoch, loss.item())
optimizer.zero_grad() # 梯度清理0
loss.backward() # 反向传播
optimizer.step() # 更新所有权重(参数)
# 我们来坐下测试
import numpy as np
import matplotlib.pyplot as plt
x = np.linspace(0, 10, 200) # 0-10采200个点
x_t = torch.Tensor(x).view((200, 1)) # 把他变成一个200行一列的矩阵
y_t = model(x_t) # 张量送到我们训练的模型
y = y_t.data.numpy() # 拿出y的数据
plt.plot(x, y)
plt.plot([0, 10], [0.5, 0.5], c='r')
plt.xlabel('Hours')
plt.ylabel('Probability of Pass')
plt.grid()
plt.show()
8. 多维输入 Multiple Dimension Input
8.1 概念
先看数据集(糖尿病)
Simple 样本;Feature 特征
8.2 设计
Sigmoid 函数采用元素方式。
把输入维度改成8;把输出维度改成1(8个维度,就是八个参数变量影响;N组数据)
这个矩阵是N维空间映射到M维空间的一种变换
我们的目标是找一个8维空间到1维空间的非线性的空间变化
其实对于神经网络来说就是这样,逐步降维从而减少神经元也就是高复杂度,并且学习能力不能太强(否则过拟合),要有好的泛化能力:
8.3 代码实现
直接降维1维
逐步降维,那么我们构造神经网络:
第一步准备数据集:
第二步构造模型:
第三步构造损失和优化器:
第四步训练:
# 导入功能包
import torch
import numpy as np
# 1准备数据
xy = np.loadtxt('diabetes.csv.gz', delimiter=',', dtype=np.float32) # 逗号作为分隔符;指定数据类型
x_data = torch.from_numpy(xy[:,:-1]) # 选择所有行;选择除去最后一列的所有列
y_data = torch.from_numpy(xy[:, [-1]]) # 选择所有行;只选择最后一列(用中括号的意思是为矩阵)
# from_numpy会根据里面的数据创造两个tensor出来
# 2.创建模型类
class Model(torch.nn.Module):
def __init__(self): # 构造函数,做初始化对象默认调用的函数
super(Model, self).__init__() # 调用父类构造
self.linear1 = torch.nn.Linear(8, 6) # 八维降六维
self.linear2 = torch.nn.Linear(6, 4) # 六维降四维
self.linear3 = torch.nn.Linear(4, 1) # 四维降一维
self.sigmoid = torch.nn.Sigmoid() # 激活函数
def forward(self, x): # 前馈计算
x = self.sigmoid(self.linear1(x)) # 可调用函数,求完前面线性的值再应用到sigmoid里当中
x = self.sigmoid(self.linear2(x))
x = self.sigmoid(self.linear3(x))
return x
model = Model()
# 3. 构造损失和优化器
criterion = torch.nn.BCELoss(size_average=True) # 构造损失函数;求均值
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 构造优化器,实现随机梯度下降(可选的动量)
# 4.训练100次
for epoch in range(100):
# 前馈
y_pred = model(x_data) # 前馈过程算y预测
loss = criterion(y_pred, y_data) # 算损失函数
print(epoch, loss.item())
# 反馈
optimizer.zero_grad() # 梯度清理0
loss.backward() # 反向传播
# 更新
optimizer.step() # 更新所有权重(参数)
8.4 补充要点
其他类型激活函数:
其图像可以通过这个链接看到:https://dashee87.github.io/data%20science/deep%20learning/visualising-activation-functions-in-neural-networks/
我们可以通过查看pytorch文档,查看pytorch里有哪些激活函数可以调用:https://pytorch.org/docs/stable/nn.html#non-linear-activations-weighted-sum-nonlinearity
对于改激活函数,只改这里点即可了:
9. 数据集和数据加载器 Dataset and DataLoader
9.1 概念
Terminology: Epoch, Batch-Size, Iterations 术语:纪元、批量大小、迭代
epoch:所有训练示例的一次前向传递和一次反向传递。
batch-size:一次向前向后传递中的训练示例数。
iterations:传递次数,每次传递使用 [batch size] 的示例数。
内层循环每次迭代执行一次mini-batch
Shuffle是打乱顺序;Loader是分组
9.2 设计
数据集是一个抽象类。 我们可以定义我们的类继承自这个类
DataLoader是一个帮助我们在PyTorch中加载数据的类
DiabetesDataset继承自抽象类Dataset
表达式 dataset[index] 将调用这个神奇函数。
这个神奇的函数返回数据集的长度。
使用批量大小、洗牌、进程号初始化加载程序。(数据集对象;小批量容量;是否打乱;多线程)
注意在windows下:
多处理的实现在Windows上是不同的,它使用spawn而不是fork。(RuntimeError:在当前进程完成其引导阶段之前,试图启动一个新进程。这可能意味着您没有使用fork来启动子进程,并且您忘记在主模块中使用适当的习惯用法:if_name_== ‘main’:freeze_support()如果程序不打算冻结以产生可执行文件,则可以省略"freeze_support()"行。)
因此,我们必须用if子句包装代码,以防止代码多次执行。
所以是:
9.3 代码实现
# 导入功能包
import torch
import numpy as np
from torch.utils.data import Dataset, DataLoader
# 1.准备数据集
class DiabetesDataset(Dataset):
def __init__(self, filepath): # filepath是路径(构造函数,做初始化对象默认调用的函数)
xy = np.loadtxt(filepath, delimiter=',', dtype=np.float32) # 逗号作为分隔符;指定数据类型
self.len = xy.shape[0] # 将数据集的个数,也就是N拿出来
self.x_data = torch.from_numpy(xy[:, :-1]) # 选择所有行;选择除去最后一列的所有列
self.y_data = torch.from_numpy(xy[:, [-1]]) # 选择所有行;只选择最后一列(用中括号的意思是为矩阵)
# from_numpy会根据里面的数据创造两个tensor出来
def __getitem__(self, index): # 根据索引返回数据样本
return self.x_data[index], self.y_data[index] # 返回的是矩阵
def __len__(self): # 将数据集的个数
return self.len
dataset = DiabetesDataset('diabetes.csv.gz') # 构造数据对象,数据文件路径送过去
train_loader = DataLoader(dataset=dataset, batch_size=32, shuffle=True, num_workers=0) # 数据集对象;小批量容量;是否打乱;多线程
# 2.创建模型类
class Model(torch.nn.Module):
def __init__(self): # 构造函数,做初始化对象默认调用的函数
super(Model, self).__init__() # 调用父类构造
self.linear1 = torch.nn.Linear(8, 6) # 八维降六维
self.linear2 = torch.nn.Linear(6, 4) # 六维降四维
self.linear3 = torch.nn.Linear(4, 1) # 四维降一维
self.sigmoid = torch.nn.Sigmoid() # 激活函数
def forward(self, x): # 前馈计算
x = self.sigmoid(self.linear1(x)) # 可调用函数,求完前面线性的值再应用到sigmoid里当中
x = self.sigmoid(self.linear2(x))
x = self.sigmoid(self.linear3(x))
return x
model = Model()
# 3. 构造损失和优化器
criterion = torch.nn.BCELoss(size_average=True) # 构造损失函数;求均值
optimizer = torch.optim.SGD(model.parameters(), lr=0.01) # 构造优化器,实现随机梯度下降(可选的动量)
# 4.训练
if __name__ == '__main__':
for epoch in range(100):
for i, data in enumerate(train_loader, 0): # train_loader的x,y放入了data里,0是起始索引
# 1. 准备数据集
inputs, labels = data # 先把输入x和标签y拿出来
# 2. 前馈
y_pred = model(inputs) # 前馈过程算y预测
loss = criterion(y_pred, labels) # 算损失函数
print(epoch, i, loss.item())
# 3. 反馈
optimizer.zero_grad() # 优化器清零
loss.backward() # 梯度清理0
# 4. 优化
optimizer.step() # 优化
10. 多分类问题 Softmax分类器 Softmax Classifier
10.1 概念
之前我们是这样的(二分类):
那么现在多分类(要求概率和为1,每个概率是≥0的)所以要用softmax:
Softmax层:
假设 𝑍𝑙 ∈ ℝ𝐾 是最后一个线性层的输出,即 Softmax 函数(zl表示第l层的输出是最后的输出)
用指数的原因是因为指数的幂大于0
举个例子:
那我们的损失函数会有怎样的改变:
10.2 设计
Numpy 中的交叉熵
PyTorch 中的交叉熵 LongTensor([0])第0个标签
我们可以举个参数的例子来看损失的比较哪个好:
10.3 代码实现
我们拿MINIST来举例子实现:
依然是老四步
参数分别为平均值和标准差。 它使用以下公式:
数据参数为:样本数、通道数、W、H
首先将N12828变为N784的矩阵
线性变化为512
然后激活函数激活一下,接着降、激活,降、激活,降到分为几类
# 0.导入功能包
import torch
# 数据集相关包
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
# 使用relu激活函数包
import torch.nn.functional as F
# 用于构建优化器的包
import torch.optim as optim
# 1.准备数据
batch_size = 64 # 设置batchsize,就是批量容量大小
# transform是将 PIL(pillow) 图像转换为张量;神经网络希望输入是比较小的、数在0-1之间并且遵循正态分布的
transform = transforms.Compose([transforms.ToTensor(), # 图像转换为张量(通道*宽*高)
transforms.Normalize((0.1307, ), (0.3081, )) # 参数分别为平均值和标准差(对这个样本算出来的数值,不是随便来的)
])
train_dataset = datasets.MNIST(root='../dataset/mnist/', # 路径
train=True, # 是否为训练集
download=True, # 是否下载
transform=transform) # transform是什么
train_loader = DataLoader(train_dataset,
shuffle=True,
batch_size=batch_size)
test_dataset = datasets.MNIST(root='../dataset/mnist/',
train=False,
download=True,
transform=transform)
test_loader = DataLoader(test_dataset,
shuffle=False,
batch_size=batch_size)
# 2.构建模型
class Net(torch.nn.Module):
def __init__(self): # 构造函数,做初始化对象默认调用的函数
super(Net, self).__init__() # 调用父类构造
self.l1 = torch.nn.Linear(784, 512) # 784到512
self.l2 = torch.nn.Linear(512, 256) # 512到256
self.l3 = torch.nn.Linear(256, 128) #256到128
self.l4 = torch.nn.Linear(128, 64) # 128到64
self.l5 = torch.nn.Linear(64, 10) # 64到10
def forward(self, x): # 784到512
x = x.view(-1, 784) # 将N*1*28*28变为N*784的矩阵(view是改为张量的形状,设置-1是将来自动去算值是多少)
x = F.relu(self.l1(x)) # 用relu对每一层进行激活
x = F.relu(self.l2(x))
x = F.relu(self.l3(x))
x = F.relu(self.l4(x))
return self.l5(x) # 最后一层不做激活
model = Net() # 定义为model模型
# 3. 构造损失和优化器
criterion = torch.nn.CrossEntropyLoss() # 构造损失函数
optimizer = optim.SGD(model.parameters(), lr=0.01, momentum=0.5) # 构造优化器,实现随机梯度下降(可选的动量);设置带冲量的优化训练过程,因为数据集较大
# 4.训练和测试(将每轮循环封装到函数当中)
def train(epoch):
running_loss = 0.0
for batch_idx, data in enumerate(train_loader, 0): # 从数据里拿出来x,y放入了data里,0是起始索引
inputs, target = data # 输入与输出存入
optimizer.zero_grad() # 优化器清零
# forward + backward + update
outputs = model(inputs) # train_loader的x,y放入了data里,0是起始索引
loss = criterion(outputs, target) # 算损失函数
loss.backward() # 反馈
optimizer.step() # 优化
running_loss += loss.item() # 将累计的loss进行储存
if batch_idx % 300 == 299: # 设置每300论输出一次
print('[%d, %5d] loss: %.3f' % (epoch + 1, batch_idx + 1, running_loss / 300))
running_loss = 0.0
def test():
correct = 0 # 正确数量
total = 0 # 总数多少
with torch.no_grad(): # 使用no_grad(),在此中的操作都不会计算梯度
for data in test_loader: # 从test_loader里拿数据
images, labels = data # 输入与输出存入
outputs = model(images) # 拿完数据做预测
_, predicted = torch.max(outputs.data, dim=1) # 从第一维度不断找,拿出每一组的最大值的下标;返回下标和最大值
total += labels.size(0) # N是每个batch的样本数量,求和就是全部
correct += (predicted == labels).sum().item() # 求和比较为真的个数
print('Accuracy on test set: %d %%' % (100 * correct / total))
# 主函数运行
if __name__ == '__main__':
for epoch in range(10): # 训练10论(一轮训练一轮测试)
train(epoch)
test()
10.4 补充要点
交叉熵损失和nll损失之间的差别:
https://pytorch.org/docs/stable/nn.html#crossentropyloss
https://pytorch.org/docs/stable/nn.html#nllloss
补充下单多通道:
我们读进来的一般是w* h * c,在pytorch里是c * w * h(C是通道;H是高;W是宽)