一篇文章教你掌握——Pytorch深度学习实践基础


前言:
首先感谢大家的支持,感谢新老粉及本人达成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

  1. 首先确定自己显卡的算力-确定自己显卡型号(通过任务管理器可以看到)
  2. 确定自己的可选择的CUDA Runtime Version
  3. 确保自己的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

  1. 安装显卡驱动
    上英伟达官网,选择驱动,根据我们从任务管理器选择的显卡型号选择驱动(其中notebook意思是笔记本),比如我的是:
    在这里插入图片描述
    安装后我的变成了12.4更新为了最新版本
    在这里插入图片描述

2.6 安装PyTorch

官网确定CUDA Runtime版本我确定了12.1
可以从官网确定命令安装
在这里插入图片描述
也可以通过添加镜像源
在这里插入图片描述
在这里插入图片描述
先进入我们的虚拟环境
然后复制命令安装,查看是否正确
在这里插入图片描述
conda list 查看下功能包里是否有了pytorch
在这里插入图片描述
对于历史版本的pytorch在这里:
在这里插入图片描述

2.7 验证pytorch是否安装成功

  1. 进入对应的虚拟环境
  2. conda list 查看是否有没有pytorch或者torch
  3. 输入python 进入python环境中
  4. 输入import torch
  5. 输入torch.cuda.is_available() #pytorch验证是否使用电脑的gpu
  6. 显示true则安装成功
  7. print(torch.__version __) # 显示pytorch版本
    在这里插入图片描述

3. 线性模型 Linear Model

在我们做科研的步骤当中,遵循:

  1. 准备数据集 DataSet
  2. 准备模型选择或者说模型设计 Model
  3. 训练 Training
  4. 推理 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 设计

  1. Chain Rule – 1. Create Computational Graph (Forward) 链式法则 – 1. 创建计算图(正向)
  2. Local Gradient 局部梯度
  3. Given gradient from successive node 给定连续节点的梯度
  4. Use chain rule to compute the gradient (Backward) 使用链式法则计算梯度(向后)

在这里插入图片描述
举个例子,f=xw:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
我们来看下完整的序列图,前馈加反馈(其中下面的导数是求局部梯度)
在这里插入图片描述
对于y=x
w+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 张量。

  1. 准备数据集
    在这里插入图片描述
    在这里插入图片描述
    在这里插入图片描述
  2. 设计模型(由求导变为构造好计算图,自动求导)
    在这里插入图片描述
    !!!首先将模型构建为一个类:
    我们的模型类应该继承自 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)
  1. 构造损失函数和优化器
    MSELoss是
    在这里插入图片描述
    在这里插入图片描述
    优化器是告诉哪的tensor做优化,告诉优化器哪些参数需要进行随机梯度下降(实现随机梯度下降(可选的动量)),parameters这个参数不管模型多复杂,都能找到所有的参数;lr是学习率
    在这里插入图片描述
    在这里插入图片描述
  2. 训练过程
    .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是宽)
在这里插入图片描述

  • 25
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

啥都鼓捣的小yao

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值