PyTorch环境的配置和安装

conda常用命令

一、conda命令

conda 命令可以用来:

  • 创建新的 conda 环境。
  • 在现有的 conda 环境中安装/升级包。
  • 查询/搜索 Anaconda 包的索引和当前 Anaconda 的安装。

Tip: 我们可以将前面带有两个短线(--)的常用命令进行缩写(并不是所有的都可以缩写),方法是取一个短线和选项的首字母。例如 --name 可以缩写成 -n--envs 可以缩写成 -e

使用 conda -V 以查看当前的 conda 版本。

我们可以通过命令行来寻求相应的帮助,即

1
conda --help

当然也可以使用缩写

1
conda -h

如果我们想要进一步查看 install 命令该如何使用,只需执行

1
conda install -h

二、conda info

conda info 可以用来查看相关信息,其格式如下:

1
conda info [-a] [--base] [-e] [-s] [--unsafe-channels]

-a, -all: 查看所有信息(不常用)。


–base:查看基环境所在路径。

1
conda info --base

-e, –envs:列出当前所有的 conda 环境。

1
conda info -e

-s, –system:列出所有的环境变量(不常用)。


–unsafe-channels:查看令牌公开的频道。

1
conda info --unsafe-channels

三、conda create

conda create 可以用来创建新的 conda 环境,其格式如下:

1
2
conda create [-n env_name | -p path] [--clone env_name] [-c channel_address]
[packages]

-n, –name:我们可以根据环境名称来创建一个 conda 环境,假设我们的环境名称为 my_env

1
conda create -n my_env

创建完成后,我们使用如下命令进入该环境

1
conda activate my_env

此时命令行前面多了一个 (my_env),这个是我们进入了该环境的标志

使用 conda list 命令来查看该环境下都安装了哪些包。

因为我们并没有在创建环境的时候安装包,所以结果自然是什么都没有

如果要退出该环境,只需执行

1
conda deactivate

注意: 我们只能退到基环境,在基环境下继续退出依然在基环境。


-p, –prefix:除了根据名称创建环境以外,我们还可以根据地址来创建 conda 环境。

在上面的例子中,我们创建的 my_env 环境的地址为:G:\anaconda3\envs\my_env。事实上,如果不指定地址,则所有的环境都会创建在 ...\anaconda3\envs\ 下。

若要根据地址来创建,只需执行

1
conda create -p G:\anaconda3\envs\my_env

–clone--clone 主要用来克隆(复制)现有的环境。

我们可以根据现有环境的名称来进行克隆,假设现有环境的名称为 original_env,新的环境的名称为 new_env,则只需执行

1
conda create -n new_env --clone original_env

当然我们也可以根据现有环境的地址来进行克隆:

1
conda create -n new_env --clone G:\anaconda3\envs\original_env

-c, –channel:首先来介绍一下什么是 conda 频道。

conda 频道是存储 包 的位置,安装 包 时 conda 会搜索现有的频道集合,并选取其中一个频道来安装 包。

conda 的默认频道是 https://repo.anaconda.com/pkgs/,但该频道需要付费,我们一般使用 conda-forge 这个频道来进行安装,它是免费的。

有关频道的设置这里从略,后续的章节会陆续提及。


packages:我们新创建的环境如果不安装必要的包是无法使用的,需要安装的包用空格隔开,即

1
conda create -n my_env python numpy flask

我们用该命令创建了一个叫 my_env 的环境,同时安装了三个包 pythonnumpyflask

安装完成后,可以使用 conda list 命令查看已经安装的包。

此外,我们的 python 安装的是最新版本,当然我们也可以指定版本进行安装:

1
conda create -n my_env python=3.9.7 numpy flask

四、conda install

conda install 可以用来在 conda 环境中安装相应的包,具体格式如下:

1
conda install [-n env_name | -p path] [-c channel_address] [packages]

已经介绍的选项这里就不再介绍了。

例如,我们要在 my_env 下安装 3.9.7 版本的 python。我们可以先进入该环境,然后安装:

1
2
conda activate my_env
conda install python=3.9.7

注意,这里的 = 实际上是模糊约束,详情见下图:

在这里插入图片描述

也可以直接指定环境安装:

1
2
conda install -n my_env python=3.9.7 # 根据名称
conda install -p G:\anaconda3\envs\my_env python=3.9.7 # 根据地址

再例如,安装 cvxpy 时,我们可以从指定的频道(conda-forge)进行安装

1
conda install -n my_env -c conda-forge cvxpy

五、conda remove

conda remove 可以用来移除 conda 环境中的某些包,也可以移除整个环境,具体格式如下:

1
conda remove [-n env_name | -p path] [packages] [--all]

例如,如果我们需要移除 my_env 下的 numpy,只需执行

1
conda remove -n my_env numpy

当然我们也可以移除所有包(即整个conda环境)

1
conda remove -n my_env --all

需要注意的是,conda 没有提供重命名环境的命令,我们只能先克隆一份原来的环境,然后再删除原来的环境,具体操作如下(例如将环境 a 重命名成 b):

1
2
conda create -n b --clone a
conda remove -n a --all

conda uninstallconda remove别名,我们也可以使用 conda uninstall 来移除包,方法是相同的。

六、conda list

conda list 用来列出 conda 环境中的包,具体格式如下:

1
conda list [-n env_name | -p path] [package]

conda list 只列出当前环境的所有包,若要列出其他环境的包,例如列出 my_env 下的所有包,只需执行:

1
conda list -n my_env

若要列出 my_env 中与 numpy 相关的包(准确地来说是含有 numpy 字样的包),只需执行:

1
conda list -n my_env numpy

基于此,我们可以使用 conda list 来查询当前环境是否安装了相应的包(每次只能查询一个),如果最后的结果没有列出,那就说明没有安装。

conda search 用来搜索指定的包,具体格式如下:

1
conda search [-c channel_address] [-f] [packages]

我们可以指定在 conda-forge 中搜索 numpy

1
conda search -c conda-forge numpy

事实上,上述命令会搜索到所有包含 numpy 字样的包,如果我们只想搜索 numpy 这个包,需要加上 -f--full-name 的缩写),即

1
conda search -c conda-forge -f numpy

八、conda update

conda update 用来将一系列的包升级到最新版本,具体格式如下:

1
conda update [-n env_name | -p path] [packages] [--all]

例如,如果我们想要升级 my_env 下的 numpyscipy,只需执行

1
conda update -n my_env numpy scipy

如果我们要想更新 my_env 中的所有包,则需执行

1
conda update -n my_env --all

如果要更新 conda 本身,则需执行

1
conda update conda

如果要更新 anaconda ,则需执行

1
conda update anaconda

注: 升级 Anaconda 前需要先升级 conda

九、conda config

conda config 用来配置 conda 的频道,相关信息会存储在 C:\Users\你的用户名\.condarc 文件中。

添加一个频道的格式为:

1
conda config --add channels [channel]

例如,若要添加 conda-forge 这个频道,只需执行

1
conda config --add channels conda-forge

由于国外频道速度一般不稳定,我们通常考虑使用国内的频道,例如清华镜像

考虑到 Windows 用户无法直接创建名为 .condarc 的文件,我们可以先执行

1
conda config --set show_channel_urls yes

然后再直接修改该文件。

内容如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
channels:
- defaults
show_channel_urls: true
default_channels:
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/main
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/r
- https://mirrors.tuna.tsinghua.edu.cn/anaconda/pkgs/msys2
custom_channels:
conda-forge: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
msys2: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
bioconda: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
menpo: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
pytorch: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
pytorch-lts: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud
simpleitk: https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud

修改完后,执行 conda clean -i 清除索引缓存,保证用的是镜像站提供的索引。

设置完后,我们可以执行

1
conda config --get channels

来查看已经添加的频道。

配置环境

创建一个虚拟环境

1
2
conda create -n pytorch python=3.10
conda activate pytorch

安装pytorch

从官网复制需要的指令https://pytorch.org/

1
conda install pytorch torchvision torchaudio cpuonly -c pytorch #cpu版本

虚拟环境中安装jupyter

1
2
conda install jupyter notebook	
jupyter notebook #启动

image

按住Shift + 回车 运行并切换到下一行

Python两个重要函数

两个函数

dir函数

能让我们知道工具箱,以及工具箱中的分隔区有什么东西

help函数

能让我们知道每个工具箱中的工具是如何使用的。

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# _*_coding:utf-8_*_
#python学习两大法宝级函数
# 内置函数
#dir() 查看函数库里有哪些可以使用的工具
#help() 查看官方文档对 函数库 特定函数 的解释

import torch
# print(dir(torch))
# import numpy
# print(dir(numpy))

torch.cuda.is_available()
# print(dir(torch.cuda))

help(torch.cuda.is_available) #值得注意的是:在帮助函数里,对特定函数只写函数名,不要加括号

实战操作

image

image

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
# _*_coding:utf-8_*_
#pytorch 如何加载数据 #这里用了蚂蚁和蜜蜂的数据集

#pytorch中,对于加载数据,主要有两个工具集合 Dataset Dataloader

#Dataset 提供一种方式去获取数据及其label
'''将原始的数据集存放成一个标准的形式(符合pytorch库要求的形式)'''
'''
样本:sample data label sample1 ['data1','label1']
0013035.jpg : ['0013035.jpg','ants']
'''
#label 标签的意思
#如何获取每一个数据及其label
#告诉我们一共有多少个数据

#Dataloader 为后面的网络提供不同的数据形式
'''它可以将一个完整的数据集 整理成一个一个batch的形式'''
'''
batch 批 批量
batch_size = 12

假设一共有120张图片 batch_size=12 那么就会有10个batch

为什么要划分batch?
GPU运算能力很强 具体来说就是 它会有好多个计算核心 假如说GPU有12计算核心,一张图片的计算需要1个计算核心工作.一个step
betch_size=1 一个step 只能利用一个计算核心 其它的就闲着了
我们为了尽可能的利用满GPU的能力 即尽可能的利用所有的计算核心 所以说我们划分成batch
也就是说 我们现在计算的尺度 就从一张图片转化成了一个batch
假如batch_size=12,那么一个step就会利用12个计算核心 跑满GPU,以此来提升模型训练的效率 减小模型训练的时间
'''
#将数据集文件引入项目文件之中
#右键,去往项目所在的文件夹,粘贴数据集文件夹进去

#来用jupyter 查看一下数据加载的官方文档
from torch.utils.data import Dataset
#意思是从torch工具箱的常用工具的处理数据的工具中 引入Dataset工具集
help(Dataset) #第一种查看官方文档的方式
# Dataset?? #第二种查看官方文档的方式 (jupyternotebook 特有的方式)

Pycharm,jupyter使用场景

以一个错误的程序为例子

image

python文件

一个块一个快递执行

用于存储一些代码信息

image

python控制台

常用:一行一行的执行

也可以以块为单位运行,但是报错信息较多,影响美观

利用python控制到进行一个小区域的调试

image

jupyter

以任意行为块运行

jupter用于一个小的项目,或者一个小的区域的调试

image

对比

①是遇到错误时的状态

②是修改错误后运行的状态

image

PyTorch加载数据

Dataset

  • 提供一种方式去获取数据及其lable
    • 如何获取每一个数据及其lable
    • 告诉我们总共有多少的数据

Dataloader

  • 为后面的网络提供不同的数据形式

image

Dataset实战

通过两个函数查询相应用法

image

使用pycharm的控制台可以方便的查看每个变量详细的值和信息(右侧)

image

代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import os
from torch.utils.data import Dataset
from PIL import Image

class mydata(Dataset):
def __init__(self,root_dir,label_dir):
self.root_dir = root_dir
self.label_dir = label_dir
self.path = os.path.join(self.root_dir,self.label_dir)
self.img_path = os.listdir(self.path)

def __getitem__(self, idx):
img_name = self.img_path[idx]
img_item_path = os.path.join(self.root_dir,self.label_dir,img_name)
img = Image.open(img_item_path)
label = self.label_dir
return img,label

def __len__(self):
return len(self.img_path)

root_dir = "dataset/train"
ants_label_dir = "ants"
bees_label_dir = "bees"
ants_dataset = mydata(root_dir,ants_label_dir)
bees_dataset = mydata(root_dir,bees_label_dir)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# _*_coding:utf-8_*_
#重新设置数据集架构
#ants_image bees_image ants_label bees_label
#并给这两个label文件夹中进行重写

#主要利用python中的os 对系统进行访问的函数
import os

def chongxie(root_dir,target_dir,out_dir):
img_path = os.listdir(os.path.join(root_dir,target_dir))
label = target_dir.split('_')[0]

for i in img_path:
file_name = i.split('.jpg')[0]
with open(os.path.join(root_dir,out_dir,"{}.txt".format(file_name)),'w') as f:
f.write(label)
#没看懂上两行

def main_chongxie_ants():
root_dir = "6tuduidata/train"
target_dir = "ants_image" # target_dir 目标目录
out_dir = "ants_label"
chongxie(root_dir,target_dir,out_dir)

def main_chongxie_bees():
root_dir = "6tuduidata/train"
target_dir = "bees_image"
out_dir = "bees_label"
chongxie(root_dir,target_dir,out_dir)

main_chongxie_ants()
main_chongxie_bees()
#帮助
#help(target_dir.split)
#Return a list of the words in the string, using sep as the delimiter string.
#返回字符串中单词的列表,使用 sep 作为分隔符字符串。


详细分析

1.PIL知识补充:

1
2
3
from PIL import Image
img_path = r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg"
img = Image.open(img_path) #创建一个图片变量

打错了,img变量不是str类型,img_path是str类型

可以看到这个图片变量包含了很多属性

1
2
3
4
from PIL import Image
img_path = r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg" #获取图片的地址
img = Image.open(img_path) #创建一个图片变量
img.show() #展示对应位置的一个图片

image

PS:因此我们只要获取了图片的地址就可以调用图片了,如何获取图片地址呢?先获取所有图片的地址的一个列表,再通过索引获取。见下:

2.os库知识补充

1
2
3
4
5
6
7
8
from PIL import Image
img_path = r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg" #获取图片的地址
img = Image.open(img_path) #创建一个图片变量
img.show() #展示对应位置的一个图片

dir_path = "dataset/train/ants"
import os
img_path_list = os.listdir(dir_path) #获取所有图片的地址的一个列表

image

1
2
3
4
5
6
7
8
9
10
from PIL import Image
img_path = r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg" #获取图片的地址
img = Image.open(img_path) #创建一个图片变量
img.show() #展示对应位置的一个图片

dir_path = "dataset/train/ants"
import os
img_path_list = os.listdir(dir_path) #获取所有图片的地址的一个列表

img_path_list[0] # '0013035.jpg'

root_dir 根目录:此处设置为”dataset/train“ ,设置训练目录的路径
label_dir 标签目录:不同的标签 ,设置训练目录中的标签路径

1
2
3
4
import os
root_dir = "dataset/train"
table_dir = "ants"
path = os.path.join(root_dir,table_dir) #将两个路径连接起来。PS:因为linux和windows系统下的‘/’的含义不同,但是这个方法连接的时候会自动把它们加起来而不会出错。

3.代码详细分析

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
# _*_coding:utf-8_*_
#Dataset类代码实战

#现在要做一个蚂蚁和蜜蜂的二分类问题, 已经给你了一个训练集文件 这样设置的
#需要你处理一下这个训练集 给每一个图片进行标号

import os
from torch.utils.data import Dataset
from PIL import Image

#创建一个类 叫mydata ,继承了dataset 工具集
class mydata(Dataset):
#初始化类:为了之后的getitem函数 和 idx方法 创建一些全局变量
def __init__(self,root_dir,label_dir):
self.root_dir = root_dir #创建一个全局变量 root_dir (根目录) 表示数据集中的每个图片的地址
self.label_dir = label_dir #创建一个全局变量 label_dir (标签目录) 表示数据集中每个图片的标签

#我们想要获得图片的路径地址
self.path = os.path.join(self.root_dir,self.label_dir) #创建一个全局变量 path 表示两个路径的集合,也就是将两个路径加起来

#我们想要获得这个训练集中所有图片的地址的列
self.img_path = os.listdir(self.path) #设置一个全局变量 img_path (图片的路径) 表示的是文件名的列表

#给图片加上索引
#getitem 获取项目 idx 索引 这个函数可以实现
def __getitem__(self, idx):
#从名称列表里读取对应的名称 用索引 idx
img_name = self.img_path[idx] #设置一个变量 img_name 来存取图片名称列表里的任意一个名称

#设置每一个图片的地址(根目录 + 标签目录 + 图片名)
img_item_path = os.path.join(self.root_dir,self.label_dir,img_name)

#可以打开 对应路径下代表的 图像
img = Image.open(img_item_path)
label = self.label_dir

#返回图像 和 标签
return img,label

#我们想知道这个数据集有多长
def __len__(self):
#求一下图片路径的长度
return len(self.img_path)


#下面是创建实例的过程
root_dir = "dataset/train"
ants_label_dir = "ants"
bees_label_dir = "bees"
ants_dataset = mydata(root_dir,ants_label_dir)
bees_dataset = mydata(root_dir,bees_label_dir)

#将两部分的数据集合二为一,构成训练数据集
train_dataset = ants_dataset + bees_dataset

#查看训练数据集的长度
len(train_dataset)
#返回结果:245
#查看蚂蚁数据集的长度
len(ants_dataset)
#返回结果:124
#查看蜜蜂数据集的长度
len(bees_dataset)
#返回结果:121

image

完成后将代码输入进行测试

image

image

输入ants_dataset[0]返回的是一个元组,包含了两个信息,用两个变量去接收它。

image

用train_dataset = ants_dataset + bees_dataset 实现了数据集之间的相加,这个计巧可用于之后的仿造数据集 构建子数据集 等。

image

那相加之后的下标是怎么排列的呢?直接相加,下标累加。

1
2
3
4
5
6
img,label = train_dataset[123]
img.show()
#打开的是蚂蚁
img,label = train_dataset[124]
img.show()
#打开的是蜜蜂

TensorBoard

Tensorboard的安装

引入

本来是继续学习transform,但是需要提前学习Tensorboard

tensorboard的作用:

  • 用来显示训练过程中的函数图像,以此来看损失函数,确定什么时候停止训练。
  • 用来查看某一步时的输入或者输出的状态。

image

代码

需要安装的两个函数库:tensorboard,opencv

1
2
pip install tensorboard
pip install opencv-python

使用:

1
2
3
4
5
6
7
8
from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter("logs")

#需要用到的方法
writer.add_image()
writer.add_scalar()

writer.close()

详细分析

1
2
3
4
5
6
7
8
9
#主要的函数有:writer.add_image()  writer.add_scalar() writer.close()

#从torch工具集的常用工具箱中的tensorboard工具箱中引入 SummaryWriter 类
from torch.utils.tensorboard import SummaryWriter #tensorboard 张量版 utils 实用程序

#创建实例
#一般,要将创建的实例文件存放在 logs 文件夹下
writer = SummaryWriter("logs")#创建文件夹 logs
#PS:logs 日志,SummaryWriter 摘要写作

PS:pycharm快捷键

快捷键技巧:
ctrl + / 可以来进行注释 再按一次是取消注释
ctrl + c 可以用来取消 (在python控制台 和 终端中 使用较多
ctrl + d 在写完的代码的后面 紧跟着按 可以将在下一行复制这一行的内容
ctrl + q 在写完的代码的后面 紧跟着按 可以查看官方文档
双击shift 进行查找
alt + enter 可以进行优化提示 和 自动优化
按住ctrl,然后点击要查看的函数,可以直接跳转到官方文档
ctrl + shift + y 进行翻译 默认翻译一个单词吧

add_scalar()的使用

作用:用来绘制train/val loss

函数提示

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
def add_scalar(
self,
tag,
scalar_value,
global_step=None,
walltime=None,
new_style=False,
double_precision=False,
):
"""Add scalar data to summary.

Args:
tag (str): Data identifier #图标上的标题
scalar_value (float or string/blobname): Value to save #图表上的Y轴
global_step (int): Global step value to record #图标上的X轴
walltime (float): Optional override default walltime (time.time())
with seconds after epoch of event
new_style (boolean): Whether to use new style (tensor field) or old
style (simple_value field). New style could lead to faster data loading.
Examples::

from torch.utils.tensorboard import SummaryWriter
writer = SummaryWriter()
x = range(100)
for i in x:
writer.add_scalar('y=2x', i * 2, i)
writer.close()

Expected result:

.. image:: _static/img/tensorboard/add_scalar.png
:scale: 50 %

"""

示例

1
2
3
4
5
6
7
8
from torch.utils.tensorboard import SummaryWriter  
writer = SummaryWriter("logs")

#y = x
for i in range(100):
writer.add_scalar("y=x",i,i)

writer.close()

运行后发现了logs文件夹里多了个文件,里面就是tensorboard的一些实践文件:

image

如何打开事件文件呢?

使用命令行工具

1
2
tensorboard --logdir=E:\Subjiect\Pycharm\PyTorch\logs #logdir=事件文件所在的文件夹
tensorboard --logdir=E:\Subjiect\Pycharm\PyTorch\logs --port=6007 #指定端口号,防止在合作开发时使用从而与他人冲突

image

image

将图像改为y=2x再次尝试:

1
2
3
4
5
6
7
8
from torch.utils.tensorboard import SummaryWriter  
writer = SummaryWriter("logs")

#y = 2x
for i in range(100):
writer.add_scalar("y=x",2*i,i)

writer.close()

image

发现logs文件夹有了两个文件了,再次刷新tensorboard查看:

image

问题

可以发现生成后的图像很抽象,为什么呢?

是因为它是在 y=x 这个tag所属的图像下进行了拟合 拟合成了y=2x 并且保留了拟合的过程。在图中可以看到y=x和y=2x的两个直线,以及拟合后形成的图像。

得出结论:tag是用来控制图像的。

如何解决呢?

下图为官方解决方案:

image

如果我们要形成一个新的图的话可以写一个子文件。简单来说就是重新创建一个文件用来存放logs生成的事件,在终端打开时将路径改为子文件即可。

image

add_image()的使用

作用:用来观察训练结果

函数提示

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
def add_image(
self, tag, img_tensor, global_step=None, walltime=None, dataformats="CHW"
):
"""Add image data to summary.

Note that this requires the ``pillow`` package.

Args:
tag (str): Data identifier #图像的title
img_tensor (torch.Tensor, numpy.ndarray, or string/blobname): Image data #img_tensor图像张量,这里限定了图像的类型,这几种(torch.Tensor, numpy.ndarray, or string/blobname)
global_step (int): Global step value to record #要记录的全局步长值
walltime (float): Optional override default walltime (time.time())
seconds after epoch of event
dataformats (str): Image data format specification of the form
CHW, HWC, HW, WH, etc.
Shape:
img_tensor: Default is :math:`(3, H, W)`. You can use ``torchvision.utils.make_grid()`` to
convert a batch of tensor into 3xHxW format or call ``add_images`` and let us do the job.
Tensor with :math:`(1, H, W)`, :math:`(H, W)`, :math:`(H, W, 3)` is also suitable as long as
corresponding ``dataformats`` argument is passed, e.g. ``CHW``, ``HWC``, ``HW``.

Examples::

from torch.utils.tensorboard import SummaryWriter
import numpy as np
img = np.zeros((3, 100, 100))
img[0] = np.arange(0, 10000).reshape(100, 100) / 10000
img[1] = 1 - np.arange(0, 10000).reshape(100, 100) / 10000

img_HWC = np.zeros((100, 100, 3))
img_HWC[:, :, 0] = np.arange(0, 10000).reshape(100, 100) / 10000
img_HWC[:, :, 1] = 1 - np.arange(0, 10000).reshape(100, 100) / 10000

writer = SummaryWriter()
writer.add_image('my_image', img, 0)

# If you have non-default dimension setting, set the dataformats argument.
writer.add_image('my_image_HWC', img_HWC, 0, dataformats='HWC')
writer.close()

Expected result:

.. image:: _static/img/tensorboard/add_image.png
:scale: 50 %

"""

示例

1
img_tensor (torch.Tensor, numpy.ndarray, or string/blobname)

由于其第二个参数支持的类型是有限的,我们先查看一下PIL创建的图像变量的类型:(type()来输出这个图像的格式 当然也可以直接在变量里看到)

image

发现这个类型不满足要求。

因此我们选用numpy类型,opencv读取的数据类型就是numpy型。

image

1
2
3
4
5
6
7
8
9
10
11
12
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image

writer = SummaryWriter("logs")
image_path = r"E:\Subjiect\Pycharm\PyTorch\data\train\ants_image\0013035.jpg"
image_PIL = Image.open(image_path)
image_array = np.array(image_PIL)

writer.add_image("test",image_array,1) #运行后报错

writer.close()

报错原因:

1
2
3
4
5
6
7
8
#这里要注意一个隐含的点,add.image中对于图片的shape形状 默认是(3,H,W),意思是 (3个通道,高度,宽度)
'''Shape:
img_tensor: Default is :math:`(3, H, W)`. You can use ``torchvision.utils.make_grid()`` to
convert a batch of tensor into 3xHxW format or call ``add_images`` and let us do the job.
Tensor with :math:`(1, H, W)`, :math:`(H, W)`, :math:`(H, W, 3)` is also suitable as long as
corresponding ``dataformats`` argument is passed, e.g. ``CHW``, ``HWC``, ``HW``.'''

print(image_array.shape)#查看图片信息,发现通道在第三个位置,(H, W, 3)这种类型,因此需要给add_image指定第四个参数dataformats='HWC'

image

修改后:

1
2
3
4
5
6
7
8
9
10
11
12
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image

writer = SummaryWriter("logs")
image_path = r"E:\Subjiect\Pycharm\PyTorch\data\train\ants_image\0013035.jpg"
image_PIL = Image.open(image_path)
image_array = np.array(image_PIL)

writer.add_image("test",image_array,1,dataformats='HWC')

writer.close()

运行后出现事件文件,可以查看:

image

1
2
3
4
5
6
7
8
9
10
11
12
13
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image

writer = SummaryWriter("logs")
image_path = r"E:\Subjiect\Pycharm\PyTorch\data\train\bees_image\16838648_415acd9e3f.jpg"
image_PIL = Image.open(image_path)
image_array = np.array(image_PIL)

#名称test不变,把步长改为2
writer.add_image("test",image_array,2,dataformats='HWC')

writer.close()

发现名称没变,但是步长那里有个滑块,可以左右滑动切换图片。

image

image

如果想要分成两个的话改一下名称即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
from torch.utils.tensorboard import SummaryWriter
import numpy as np
from PIL import Image

writer = SummaryWriter("logs")
image_path = r"E:\Subjiect\Pycharm\PyTorch\data\train\bees_image\17209602_fe5a5a746f.jpg"
image_PIL = Image.open(image_path)
image_array = np.array(image_PIL)

#名称改为test2
writer.add_image("test2",image_array,1,dataformats='HWC')

writer.close()

image

Transform

Transform的结构与用法

通过左下的结构可以看到transform.py文件中的各种类,各种用法,方便查询:

image

transform用法大概流程:将一个特定格式的图片用transform处理,返回一个我们需要的结果。

image

transform用法

下面我们通过 transforms.ToTensor 去看两个问题

  • transforms 在python中该如何使用
  • 为什么我们需要Tensor数据类型

transforms 在python中该如何使用

ToTensor源码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
class ToTensor:
"""Convert a ``PIL Image`` or ``numpy.ndarray`` to tensor. This transform does not support torchscript.
#将常用的图片类型都包含了,比如PIL的类型,numpy的等ndarray类型等

Converts a PIL Image or numpy.ndarray (H x W x C) in the range
[0, 255] to a torch.FloatTensor of shape (C x H x W) in the range [0.0, 1.0]
if the PIL Image belongs to one of the modes (L, LA, P, I, F, RGB, YCbCr, RGBA, CMYK, 1)
or if the numpy.ndarray has dtype = np.uint8

In the other cases, tensors are returned without scaling.

.. note::
Because the input image is scaled to [0.0, 1.0], this transformation should not be used when
transforming target image masks. See the `references`_ for implementing the transforms for image masks.

.. _references: https://github.com/pytorch/vision/tree/main/references/segmentation
"""

def __init__(self) -> None:
_log_api_usage_once(self)

def __call__(self, pic):
"""
Args:
pic (PIL Image or numpy.ndarray): Image to be converted to tensor.

Returns:
Tensor: Converted image.
"""
return F.to_tensor(pic)

def __repr__(self) -> str:
return f"{self.__class__.__name__}()"

示例: 将一个图片转为tensor数据类型

image

1
2
3
4
5
6
7
8
from PIL import Image
from torchvision import transforms

img_path = r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg"
img = Image.open(img_path)
tensor_trans = transforms.ToTensor() #
tensor_img = tensor_trans(img) #类加上()则调用__cal__方法
print(tensor_img)

image

代码思想,transforms是一个大的python程序 其中包含了各个类的 工具集
我们要阅读其中的工具集,通看工具集中各个工具的使用方式 (它们都相当于一个工具模板)
我们要创建具体的工具,通过利用 工具集中已有的工具 作为工具模板,制作特定的工具

PS:opencv的读取图片的方式,读取后的类型是numpy.ndarray类型

1
2
3
#numpy.ndarray 最常见的读取形式用 opencv
import cv2 #引入opencv
img_cv = cv2.imread(img_path) #注意,这里的 opencv 和 PIL 是等价的,都是一种图片的打开方式,所以括号里要放图片的路径

为什么我们需要Tensor数据类型

进行神经网络学习的时候必定会用到ToTensor,将我们常用的数据类型转换成tensor类型。

1
2
3
4
5
6
7
8
9
10
11
12
13
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms

img_path = r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg"
img = Image.open(img_path)
tensor_trans = transforms.ToTensor()
img_tensor = tensor_trans(img)

#接下来 我们 用tensor的方式来写入图片
writer = SummaryWriter("logs") #设立要存放事例文件的文件夹
writer.add_image("tensor_img",img_tensor)
writer.close()

image

PS:add_image中的参数img_tensor需要的类型包括了(torch.Tensor, numpy.ndarray, or string/blobname)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def add_image(self, tag, img_tensor, global_step=None, walltime=None, dataformats="CHW"):
"""Add image data to summary.

Note that this requires the ``pillow`` package.

Args:
tag (str): Data identifier
img_tensor (torch.Tensor, numpy.ndarray, or string/blobname): Image data
global_step (int): Global step value to record
walltime (float): Optional override default walltime (time.time())
seconds after epoch of event
dataformats (str): Image data format specification of the form
CHW, HWC, HW, WH, etc.
”“”

常见的transform

image

Compose

在 PyTorch 中,Compose 是一种可以将多个数据预处理操作串联起来的工具。通过将多个操作组合在一起,可以更方便地对数据进行预处理和增强。

类源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
class Compose:
"""Composes several transforms together. This transform does not support torchscript.
将多个变换组合在一起。此转换不支持torchscript。
Please, see the note below.
请看下面的注释。

Args:
transforms (list of ``Transform`` objects): list of transforms to compose. 要组合的转换列表。

Example:
>>> transforms.Compose([
>>> transforms.CenterCrop(10),
>>> transforms.PILToTensor(),
>>> transforms.ConvertImageDtype(torch.float),
>>> ])

.. note::
In order to script the transformations, please use ``torch.nn.Sequential`` as below.

>>> transforms = torch.nn.Sequential(
>>> transforms.CenterCrop(10),
>>> transforms.Normalize((0.485, 0.456, 0.406), (0.229, 0.224, 0.225)),
>>> )
>>> scripted_transforms = torch.jit.script(transforms)

Make sure to use only scriptable transformations, i.e. that work with ``torch.Tensor``, does not require
`lambda` functions or ``PIL.Image``.

"""

示例

Compose 的基本用法是将多个数据预处理操作传入 Compose 中,然后将 Compose 对象应用到数据上。下面是一个简单的例子:

1
2
3
4
5
6
7
8
9
10
11
12
from torchvision import transforms

# 定义多个数据预处理操作
transforms_list = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 应用到数据上
image = transforms_list(image)

在这个例子中,我们首先定义了一个包含多个数据预处理操作的列表 transforms_list,其中包括 ResizeCenterCropToTensorNormalize 等操作。然后,我们将这个列表传入 Compose 中,创建了一个 Compose 对象 transforms_list。最后,我们将这个 Compose 对象应用到图像数据上,对图像进行了预处理和增强。

在实际使用中,Compose 可以和其他 PyTorch 中的数据预处理和增强操作结合使用,例如 RandomCrop、RandomHorizontalFlip 等等。通过组合多个操作,可以灵活地对数据进行预处理和增强,以适应不同的任务和场景。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
from torchvision import transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter

img = Image.open(r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg")

writer = SummaryWriter("logs")

trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)


# 定义多个数据预处理操作
transforms_list = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 应用到数据上
image = transforms_list(img_tensor)


writer.add_image("transform",img_tensor)
writer.close()

上面这段代码有一个问题在于,transforms.ToTensor() 已经将 PIL Image 对象转换为 Tensor 对象,而后面的 transforms_list 又对 Tensor 进行了变换。这样做会导致图像的像素值范围超过了 [0,1],并且均值和方差的归一化处理也不再正确。因此,如果您想要应用多个变换,需要确保它们的顺序正确,以确保每个变换都作用于正确的数据类型和数据范围上。

以下是一个修正后的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
from torchvision import transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter

img = Image.open(r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg")

writer = SummaryWriter("logs")

# 定义多个数据预处理操作
transforms_list = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=[0.485, 0.456, 0.406], std=[0.229, 0.224, 0.225])
])

# 应用到数据上
image = transforms_list(img)

writer.add_image("transform",image)
writer.close()

在这个示例中,我们首先定义了一个包含多个数据预处理操作的列表 transforms_list,其中包括 ResizeCenterCropToTensorNormalize 等操作。然后,我们将这个列表传入 Compose 中,创建了一个 Compose 对象 transforms_list。最后,我们将这个 Compose 对象应用到图像数据上,对图像进行了预处理和增强。最后,我们将处理后的图像作为参数传给 writer.add_image() 方法,将图像保存到 TensorBoard 日志中。

image

Normalize

在 PyTorch 中,transforms.Normalize() 可以用来对图像数据进行归一化处理。

具体来说,这个函数可以将每个通道的像素值减去均值,并除以标准差,以使图像数据的像素值分布在接近于 0 的范围内。

1
output[channel] = (input[channel] - mean[channel]) / std[channel]

类源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Normalize(torch.nn.Module):
"""Normalize a tensor image with mean and standard deviation.使用平均值和标准偏差对张量图像进行归一化。
This transform does not support PIL Image.此转换不支持PIL图像
Given mean: ``(mean[1],...,mean[n])`` and std: ``(std[1],..,std[n])`` for ``n``
channels, this transform will normalize each channel of the input
``torch.*Tensor`` i.e.,
``output[channel] = (input[channel] - mean[channel]) / std[channel]`` #运算公式

.. note::
This transform acts out of place, i.e., it does not mutate the input tensor.
这个变换的作用是不合时宜的,也就是说,它不会使输入张量发生变化。

Args:
mean (sequence): Sequence of means for each channel.每个通道的手段顺序。
std (sequence): Sequence of standard deviations for each channel.每个通道的标准偏差顺序。
inplace(bool,optional): Bool to make this operation in-place.

"""

def __init__(self, mean, std, inplace=False):
super().__init__()
_log_api_usage_once(self)
self.mean = mean
self.std = std
self.inplace = inplace

def forward(self, tensor: Tensor) -> Tensor:
"""
Args:
tensor (Tensor): Tensor image to be normalized.

Returns:
Tensor: Normalized Tensor image.
"""
return F.normalize(tensor, self.mean, self.std, self.inplace)

def __repr__(self) -> str:
return f"{self.__class__.__name__}(mean={self.mean}, std={self.std})"

示例

下面是一个简单的示例,演示了如何使用 transforms.Normalize() 对图像数据进行归一化处理:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import torch
import torchvision.transforms as transforms
from PIL import Image

# 打开一张图片
img = Image.open("example.jpg")

# 定义均值和标准差
mean = [0.485, 0.456, 0.406]
std = [0.229, 0.224, 0.225]

# 定义图像预处理操作
transform = transforms.Compose([
transforms.Resize(256),
transforms.CenterCrop(224),
transforms.ToTensor(),
transforms.Normalize(mean=mean, std=std)
])

# 应用到数据上
img = transform(img)

# 打印图像的均值和标准差
print(torch.mean(img, axis=(1, 2)))
print(torch.std(img, axis=(1, 2)))

在这个示例中,我们首先打开了一张图片,并定义了图像的均值和标准差。然后,我们使用 transforms.Compose() 定义了一系列图像预处理操作,其中包括 ResizeCenterCropToTensorNormalize 等操作。最后,我们将这个操作应用到图像上,并打印出归一化后的图像的均值和标准差。

需要注意的是,归一化的均值和标准差需要与训练数据集的均值和标准差相同,否则可能会影响模型的训练效果。因此,在实际使用中,应该根据训练数据集的均值和标准差来计算归一化参数,而不是使用固定的值。

PS:什么是图像数据的归一化

图像数据的归一化是指将图像数据的像素值缩放到一个特定的范围内,以使得不同图像的像素值具有可比性和可解释性。通常情况下,图像数据的归一化会将像素值转化为 [0,1] 或者 [-1,1] 的范围内。

图像数据的归一化通常是深度学习中的一个重要预处理步骤。对于深度学习模型而言,输入数据的归一化可以使得训练更加稳定和高效,从而提高模型的准确性和泛化能力。

具体来说,在图像分类任务中,通常会对图像数据进行如下的归一化处理:

  1. 将图像数据转化为 Tensor 对象。
  2. 对每个通道的像素值减去均值。
  3. 对每个通道的像素值除以标准差。

这个处理过程可以使用 PyTorch 中的 transforms.Normalize() 方法来实现。通过这种方式进行归一化可以使得图像数据的均值为 0,标准差为 1,从而使得不同图像的像素值具有可比性,同时也能提高深度学习模型的训练效果和泛化能力。

需要注意的是,归一化参数需要与训练数据集的均值和标准差相同,否则可能会影响模型的训练效果。因此,在实际使用中,应该根据训练数据集的均值和标准差来计算归一化参数,而不是使用固定的值。

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
from torchvision import transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter

img_path = "dataset/train/ants/0013035.jpg"
img = Image.open(img_path)
#之后,创建事例文件的文件夹
writer = SummaryWriter("logs")
#再者,我们将PIL转化为tensor
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img)

#根据类 来自定义归一化函数(也就是输入参数)
trans_norm = transforms.Normalize([7,9,9],[6,5,4]) #这里体现根据函数模板俩自定义函数的过程 可以自己确定参数的
#利用自定义(规定了参数)的函数,对tensor进行归一化
img_norm = trans_norm(img_tensor)
#我们想要看一下归一化前 和 归一化后的 区别
print(img_tensor[0][0][0]) #输出归一化前的,第0层,第0行,第0列 的像素
print(img_norm[0][0][0]) #输出归一化后的,第0层,第0行,第0列 的像素
#返回结果:
#tensor(0.3137)
#tensor(-0.3725)
#然后我们进行一个验算:2*0.3137-1 = -0.37260000000000004 结果是正确的

#然后我们还可以用可视化工具tensorboad来看一下,归一化后的图像的样子
#This transform acts out of place, i.e., it does not mutate the input tensor.(重点,归一化前后不改变图像的类型)
#所以可以继续写入
writer.add_image("norm",img_norm,2)
writer.close()
#然后运行来创建事例文件,之后在终端进行打开
#于是我们那就得到了恶魔蚂蚁
#之后我们可以随便调几个值,并且利用writer.add_image("norm",img_norm) 参数中的步骤参数 创建对应参数的事例文件 并按步骤进行展示
#若图片不是RGB,而是三维的,可以使用 img = Image.open(img_path).convert('RGB')转化成 RGB 三维的

Resize

在 PyTorch 中,transforms.Resize() 可以用来对图像进行缩放操作。具体来说,这个函数可以将图像的大小调整到指定的尺寸,以适应深度学习模型的输入要求。

类源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
class Resize(torch.nn.Module):
"""Resize the input image to the given size.
If the image is torch Tensor, it is expected
to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions

.. warning::
The output image might be different depending on its type: when downsampling, the interpolation of PIL images
and tensors is slightly different, because PIL applies antialiasing. This may lead to significant differences
in the performance of a network. Therefore, it is preferable to train and serve a model with the same input
types. See also below the ``antialias`` parameter, which can help making the output of PIL images and tensors
closer.

Args:
size (sequence or int): Desired output size. If size is a sequence like
(h, w), output size will be matched to this. If size is an int,
smaller edge of the image will be matched to this number.
i.e, if height > width, then image will be rescaled to
(size * height / width, size).

.. note::
In torchscript mode size as single int is not supported, use a sequence of length 1: ``[size, ]``.
interpolation (InterpolationMode): Desired interpolation enum defined by
:class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.BILINEAR``.
If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` and
``InterpolationMode.BICUBIC`` are supported.
For backward compatibility integer values (e.g. ``PIL.Image[.Resampling].NEAREST``) are still accepted,
but deprecated since 0.13 and will be removed in 0.15. Please use InterpolationMode enum.
max_size (int, optional): The maximum allowed for the longer edge of
the resized image: if the longer edge of the image is greater
than ``max_size`` after being resized according to ``size``, then
the image is resized again so that the longer edge is equal to
``max_size``. As a result, ``size`` might be overruled, i.e the
smaller edge may be shorter than ``size``. This is only supported
if ``size`` is an int (or a sequence of length 1 in torchscript
mode).
antialias (bool, optional): antialias flag. If ``img`` is PIL Image, the flag is ignored and anti-alias
is always used. If ``img`` is Tensor, the flag is False by default and can be set to True for
``InterpolationMode.BILINEAR`` and ``InterpolationMode.BICUBIC`` modes.
This can help making the output for PIL images and tensors closer.
"""


示例

下面是一个简单的示例,演示了如何使用 transforms.Resize() 对图像进行缩放操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torchvision.transforms as transforms
from PIL import Image

# 打开一张图片
img = Image.open("example.jpg")

# 定义图像预处理操作
transform = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor()
])

# 应用到数据上
img = transform(img)

# 打印图像的大小
print(img.shape)

在这个示例中,我们首先打开了一张图片,并使用 transforms.Compose() 定义了一系列图像预处理操作,其中包括 ResizeToTensor 等操作。其中,Resize 操作将图像的大小调整为 (224, 224),ToTensor 操作将图像转换为 Tensor 对象。最后,我们将这个操作应用到图像上,并打印出处理后的图像的大小。

需要注意的是,在实际使用中,调整图像的大小需要根据具体的需求来进行。通常情况下,应该根据深度学习模型的输入要求来决定图像的尺寸。在对图像进行缩放操作时,可以将图像的长边缩放到指定的大小,然后按比例缩放短边,以保持图像的宽高比不变。这样做可以避免图像形变和信息丢失,从而提高模型的准确性和泛化能力。

RandomCrop

在 PyTorch 中,transforms.RandomCrop() 可以用来对图像进行随机裁剪操作。具体来说,这个函数可以将图像随机裁剪到指定的尺寸,以增加数据集的多样性和泛化能力。

类源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
class RandomCrop(torch.nn.Module):
"""Crop the given image at a random location.
If the image is torch Tensor, it is expected
to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions,
but if non-constant padding is used, the input is expected to have at most 2 leading dimensions

Args:
size (sequence or int): Desired output size of the crop. If size is an
int instead of sequence like (h, w), a square crop (size, size) is
made. If provided a sequence of length 1, it will be interpreted as (size[0], size[0]).
padding (int or sequence, optional): Optional padding on each border
of the image. Default is None. If a single int is provided this
is used to pad all borders. If sequence of length 2 is provided this is the padding
on left/right and top/bottom respectively. If a sequence of length 4 is provided
this is the padding for the left, top, right and bottom borders respectively.

.. note::
In torchscript mode padding as single int is not supported, use a sequence of
length 1: ``[padding, ]``.
pad_if_needed (boolean): It will pad the image if smaller than the
desired size to avoid raising an exception. Since cropping is done
after padding, the padding seems to be done at a random offset.
fill (number or tuple): Pixel fill value for constant fill. Default is 0. If a tuple of
length 3, it is used to fill R, G, B channels respectively.
This value is only used when the padding_mode is constant.
Only number is supported for torch Tensor.
Only int or tuple value is supported for PIL Image.
padding_mode (str): Type of padding. Should be: constant, edge, reflect or symmetric.
Default is constant.

- constant: pads with a constant value, this value is specified with fill

- edge: pads with the last value at the edge of the image.
If input a 5D torch Tensor, the last 3 dimensions will be padded instead of the last 2

- reflect: pads with reflection of image without repeating the last value on the edge.
For example, padding [1, 2, 3, 4] with 2 elements on both sides in reflect mode
will result in [3, 2, 1, 2, 3, 4, 3, 2]

- symmetric: pads with reflection of image repeating the last value on the edge.
For example, padding [1, 2, 3, 4] with 2 elements on both sides in symmetric mode
will result in [2, 1, 1, 2, 3, 4, 4, 3]
"""

示例

下面是一个简单的示例,演示了如何使用 transforms.RandomCrop() 对图像进行随机裁剪操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torchvision.transforms as transforms
from PIL import Image

# 打开一张图片
img = Image.open("example.jpg")

# 定义图像预处理操作
transform = transforms.Compose([
transforms.RandomCrop((224, 224)),
transforms.ToTensor()
])

# 应用到数据上
img = transform(img)

# 打印图像的大小
print(img.shape)

在这个示例中,我们首先打开了一张图片,并使用 transforms.Compose() 定义了一系列图像预处理操作,其中包括 RandomCropToTensor 等操作。其中,RandomCrop 操作将图像随机裁剪到 (224, 224) 的大小,ToTensor 操作将图像转换为 Tensor 对象。最后,我们将这个操作应用到图像上,并打印出处理后的图像的大小。

需要注意的是,在实际使用中,随机裁剪的大小应该根据具体的需求来进行。通常情况下,可以将图像随机裁剪到稍微大于深度学习模型输入尺寸的大小,然后按照固定的比例缩放到指定的大小,以增加数据集的多样性和泛化能力。同时,需要注意不要裁剪掉图像中重要的信息,从而影响模型的准确性和泛化能力。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import torchvision.transforms as transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter

# 打开一张图片
img = Image.open(r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg")

writer = SummaryWriter("logs")

# 定义图像预处理操作
transform = transforms.Compose([
transforms.RandomCrop((224, 224)),
transforms.ToTensor()
])

# 应用到数据上
img = transform(img)

# 打印图像的大小
print(img.shape)

writer.add_image("transform",img)
writer.close()

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import torchvision.transforms as transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter

# 打开一张图片
img = Image.open(r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg")

writer = SummaryWriter("logs")

# 定义图像预处理操作
transform = transforms.Compose([
transforms.RandomCrop((224, 224)),
transforms.ToTensor()
])

# 对图像进行操作 利用for循环进行多个拆分
for i in range(10):
img = transform(img)


writer.add_image("transform",img,i)
writer.close()

这段代码有两个错误:

  1. 在 for 循环中,每次将图像进行预处理之后,应该将结果保存到一个新的变量中,否则每次处理的结果都会覆盖原来的图像,导致后续操作出现错误。
  2. 在调用 writer.add_image() 函数时,应该将图片张量作为第一个参数传入,而不是图片路径。

下面是修改后的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
import torchvision.transforms as transforms
from PIL import Image
from torch.utils.tensorboard import SummaryWriter

# 打开一张图片
img = Image.open(r"E:\Subjiect\Pycharm\PyTorch\dataset\train\ants\0013035.jpg")

writer = SummaryWriter("logs")

# 定义图像预处理操作
transform = transforms.Compose([
transforms.RandomCrop((224, 224)),
transforms.ToTensor()
])

# 对图像进行操作 利用for循环进行多个拆分
for i in range(10):
img_transformed = transform(img)
writer.add_image("transform", img_transformed, i)

writer.close()

在修改后的代码中,我们将每次处理的结果保存到一个新的变量 img_transformed 中,并将其作为参数传递给 writer.add_image() 函数。

image

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# _*_coding:utf-8_*_
#常见的transforms
#本节我们主要学习 Resize (改变函数的高和宽,H,W,即大小) compose(流水线化transform) RandomCrop 随机裁剪
from PIL import Image
from torch.utils.tensorboard import SummaryWriter
from torchvision import transforms
img_path = "6tuduidata/train/ants_image/0013035.jpg"
img = Image.open(img_path)
#学习Resize()
'''
class Resize(torch.nn.Module):
"""Resize the input image to the given size.
If the image is torch Tensor, it is expected (重点,这里就给出了输入类型是 tensor)
to have [..., H, W] shape, where ... means an arbitrary(任意数量的) number of leading dimensions(前导维数)
#RGB: 也就为3

Args:
size (sequence(序列) or int): Desired output size. If size is a sequence like
(h, w), output size will be matched to this. If size is an int,
smaller edge of the image will be matched to this number.
i.e, if height > width, then image will be rescaled to
(size * height / width, size).
In torchscript mode size as single int is not supported, use a sequence of length 1: ``[size, ]``.
interpolation (InterpolationMode): Desired interpolation enum defined by
:class:`torchvision.transforms.InterpolationMode`. Default is ``InterpolationMode.BILINEAR``.
If input is Tensor, only ``InterpolationMode.NEAREST``, ``InterpolationMode.BILINEAR`` and
``InterpolationMode.BICUBIC`` are supported.
For backward compatibility integer values (e.g. ``PIL.Image.NEAREST``) are still acceptable.
(这里也指定了输入 可以是上个版本的 PIL)
sequence(序列):在python中,列表的表示形式为[数据1,数据2,...]
在Compose中,数据需要是 transforms 类型
所以得到,Compose([transforms参数1,transforms参数2,...])
'''
#先来根据类 模板来自定义函数
trans_resize = transforms.Resize([512,512])
#再来转化PIL 图像
img_resize = trans_resize(img)

#然后我们想来对比一下转化前后图像的变化
print(img.size)
print(img_resize)
#返回结果:
#(768, 512)
#<PIL.Image.Image image mode=RGB size=512x512 at 0x1DD130D76D0>
#显然,我们成功改变了图像的大小,并且可以知道的是,图像仍为PIL,其类型并没通过resize 而进行了改变

#然后我们想来将转换后的图像写入tensorboard 来可视化的看一下图像的变化
#先需要将图像转化为tensor类型
trans_totensor = transforms.ToTensor()
img_tensor = trans_totensor(img_resize) #这里可以改为 img_resize = img_tensor = trans_totensor(img_resize)
#之后写入图像到tensorboard #一个函数名字用了两次,后面的结果覆盖了前面的结果
#首先要先创建一个存放事例文件的文件夹 #但是还可以用compose来实现流水线操作
writer = SummaryWriter("logs")
#然后才能写入图像
writer.add_image("resize1",img_tensor)
#又忘记要关闭了!!!
writer.close()
#然乎运行,来创建事例文件,之后在终端进行查看

#然后我们就看见了,改变了大小的蚂蚁


#由之上的引入,我们来学习compose类 compose 组成
'''
class Compose:
"""Composes several transforms together. This transform does not support torchscript.
Please, see the note below.

Args:
transforms (list of ``Transform`` objects): list of transforms to compose.

Example:
>>> transforms.Compose([
>>> transforms.CenterCrop(10),
>>> transforms.ToTensor(),
>>> ])
#根据例子可以看出,compose之中的参数是用 sequence(序列) 形式存在的
#由为要注意的是,compose中的序列里的参数 前一个参数的输出 要和 下一个参数的输出 相匹配 (这也显示了compose的流水线形式)
'''
#接下来,我们使用compose 来完成之上的 resize 并且只给 resize输入 一个int 其会和图像中最短的那一条边进行对应
#利用resize类 模板来自定义函数
trans_resize_2 = transforms.Resize(512)
#利用compose类 模板来自定义函数
#注意,要先进行resize 再进行 totensor
trans_compose = transforms.Compose([trans_resize_2,trans_totensor])
#使用compose,对图像进行操作
img_resize_2 = trans_compose(img)
#写入图像 进行可视化
writer.add_image("resize2",img_resize_2)
#一定要记得关闭
writer.close()
#运行,创建事例文件,之后在终端进行打开

#然后我们就看见了一个和原来大小一样的蚂蚁 why?
#因为原来图像的最小边就是512 所以resize(512) 对应最小边 操作之后 就没有变化
#不信我们来检验一下
print(img_resize_2.size)
#卧槽,发现了compose的弊端,虽然他将两个操作 结合在了一起,进行了流水线操作 但是中间的过程是不可见的 一些功能也就因此丧失
#比如这里的,tensor类型,不好直接输出它的size了

#之后我们学习一下随机切片 RandomCrop
'''
class RandomCrop(torch.nn.Module):
"""Crop the given image at a random location.
If the image is torch Tensor, it is expected (重点:这里规定了输入的类型是 tensor)
to have [..., H, W] shape, where ... means an arbitrary number of leading dimensions,
but if non-constant padding is used, the input is expected to have at most 2 leading dimensions

Args:
size (sequence or int): Desired output size of the crop. If size is an
int instead of sequence like (h, w), a square crop (size, size) is
made. (若是只输入一个int,则不会像resize那样进行匹配,而是生成一个int,int的正方形
If provided a sequence of length 1, it will be interpreted as (size[0], size[0]).

Only int or str or tuple (元组) value is supported for PIL Image.(这里也制定了输入)
'''
#首先,我们根据RandomCrop类 模板自定义函数
trans_random = transforms.RandomCrop(512) #预计结果是裁剪出 512*512 的图片 此时可以使用于 PIL
#利用compose类 结合,先random 再 totensor 自定义函数
trans_compose_2 = transforms.Compose([trans_random,trans_totensor])
#对图像进行操作 利用for循环进行多个拆分
for i in range(10):
img_compose = trans_compose_2(img)
#利用tensorboard 进行可视化
#写入图片
writer.add_image("compose",img_compose,i) #用 i 来控制步数
#close close close
writer.close()
#运行,之后在终端打开

#于是,我们就可以看见0-9十个步骤的图片

#下面指定大小 用元组 tuple ()
tran_random_2 = transforms.RandomCrop((256,256))
trans_compose_3 = transforms.Compose([tran_random_2,trans_totensor])
for i in range(10):
img_compose_tuple = trans_compose_3(img)
writer.add_image('compose_tuple(,)',img_compose_tuple,i)
#运行,打开

#我们可以看出,出现了0-8 共9个步骤的图片 为什么是9 而不是10 呢? (256,256)
#换成 (512,512) 我们发现只有一个了0-7 8个步骤的图片了 奇奇怪怪!!!
#此问题待解答
#???


###总结一下学习方法:
#关注输入和输出类型
#输入类型,官方文档里有,一般只看没有被赋予默认值的
#输出类型,如果没给,你可以先使用后,自己找出
#方法有:单点调试
#print
#print(type())
#多看官方文档,善于看官方文档中的各种介绍



torchversion中的数据集的使用

torchvision是PyTorch提供的一个图像和视频数据集处理工具包,其中包含了常用的数据集、模型、变换等。

torchvision中的数据集是为了方便用户获取并处理常见的图像和视频数据集,比如MNIST、CIFAR、ImageNet等,用户可以直接调用相关API获取这些数据集,同时也提供了一些预处理的工具,如图像变换、数据增强等,可以在获取数据集时直接应用到数据上,简化了数据的预处理步骤。

使用torchvision中的数据集,一般需要进行以下几个步骤:

  1. 导入相关数据集API,如torchvision.datasets.CIFAR10
  2. 创建数据集实例,一般需要传入数据集的路径和一些其他参数,如train_set = torchvision.datasets.CIFAR10(root='./data', train=True, download=True)
  3. 创建数据集加载器,一般需要传入数据集实例和一些其他参数,如train_loader = torch.utils.data.DataLoader(train_set, batch_size=batch_size, shuffle=True, num_workers=num_workers)
  4. 在训练时,可以通过迭代数据集加载器获取一批数据进行训练,如for images, labels in train_loader:

具体的操作可以参考PyTorch官方文档中的示例代码。

image

image

以CIFAR数据集为例,来学习一下如何使用pytorch提供的一些数据集,并且将transforms和数据集进行联通

数据集包含了60000张32×32的图片,分为是个类比,每个类6000张,50000张训练图片,和10000张测试图片。

image

1
2
3
4
5
6
7
import torchvision

train_set = torchvision.datasets.CIFAR10(root="./dataset_2",train=True,download=True)
#训练集:root="./dataset_2"(数据集存放的地址),train=True(表示是否从训练集中创建数据集,否则从测试集创建),download=True(是否下载数据集)

test_set = torchvision.datasets.CIFAR10(root="./dataset_2",train=False,download=True)
#测试集:root="./dataset_2",train=False,download=True

image

PS:CIFAR10类源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
class CIFAR10(VisionDataset):
"""`CIFAR10 <https://www.cs.toronto.edu/~kriz/cifar.html>`_ Dataset.

Args:
root (string): Root directory of dataset where directory 数据集存放在哪里,并给一个命名一般用相对路径 ”./dataset2“
``cifar-10-batches-py`` exists or will be saved to if download is set to True.

train (bool, optional): If True, creates dataset from training set, otherwise creates from test set.
如果为True,则从训练集中创建数据集,否则从测试集创建

transform (callable, optional): A function/transform that takes in an PIL image and returns a transformed version. E.g, ``transforms.RandomCrop``
一种函数/变换,它接收PIL图像并返回变换后的版本。例如,``transforms.RandomCrop``

target_transform (callable, optional): A function/transform that takes in the target and transforms it.
接收目标并对其进行变换的函数/变换

download (bool, optional): If true, downloads the dataset from the internet and puts it in root directory. If dataset is already downloaded, it is not downloaded again.
如果为true,则从internet下载数据集并将其放在根目录中。如果数据集已经下载,则不会再次下载。

"""

示例:

1
2
3
4
5
6
import torchvision

train_set = torchvision.datasets.CIFAR10(root="./dataset_2",train=True,download=True)
test_set = torchvision.datasets.CIFAR10(root="./dataset_2",train=False,download=True)

print(test_set[0])

image

输出后发现,有两个信息输出了,第一个参数是图像 类型为PIL ,但是第二个参数我们不知道是什么,于是我们来进行一下断点调试

image

发现输出的3代表的是这张图片的target,每一个target对应一个真实的类别classes:[‘airplane’, ‘automobile’, ‘bird’, ‘cat’, ‘deer’, ‘dog’, ‘frog’, ‘horse’, ‘ship’, ‘truck’],比如这张图片的target为3,对应类别中的cat

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import torchvision
from torch.utils.tensorboard import SummaryWriter

#之后,我们想要将之前学过的transform 和 数据集进行联动,需要将图片类型转成tensor类型

#我们先来自定义transform函数,使用compose,暂且使用totensor将所有的PIL图片 都转化为 tensor 类型,并将参数传入下面。
dataset_transforms = torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])

#传入自定义的transform参数,transform=dataset_transforms,将会应用到每一张图片。
train_set = torchvision.datasets.CIFAR10("./dataset",train=True,transform=dataset_transforms,download=True)
test_set = torchvision.datasets.CIFAR10("./dataset",train=False,transform=dataset_transforms,download=True)

writer = SummaryWriter("logs")

#导入十张图片
for i in range(10):
img,target = test_set[i]
writer.add_image("test",img,i)

writer.close()

image

可以看到每张图片。

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
# _*_coding:utf-8_*_
#本节我们来以CIFAR数据集为例,来学习一下如何使用pytorch提供的一些数据集,并且将transforms和数据集进行联通

#我们先来学习一下如何使用数据集
'''
CIFAR数据集:
CIFAR10 Dataset. dataset 数据集

Parameters

root (string) – Root directory(目录) of dataset where directory cifar-10-batches-py exists or will be saved to
if download is set to True. 数据集存放在哪里,并给一个命名一般用相对路径 ”./dataset“

train (bool, optional) – If True, creates dataset from training set, otherwise creates from test set.
true 代表训练集 false 代表验证集(测试集)
transform (callable, optional) – A function/transform that takes in an PIL image
and returns a transformed version. E.g, transforms.RandomCrop

target_transform (callable, optional) – A function/transform that takes in the target and transforms it.

download (bool, optional) – If true, downloads the dataset from the internet
and puts it in root directory. If dataset is already downloaded, it is not downloaded again.
所以一般建议,这个参数始终保持为 True
#如果你从迅雷下载(只是一个压缩包),并导入了数据集,它可以帮助进行校验,然后解压缩

'''

#现在,我们来使用调用这个数据集
import torchvision
from torch.utils.tensorboard import SummaryWriter

train_set = torchvision.datasets.CIFAR10("./dataset",train=True,download=True)
test_set = torchvision.datasets.CIFAR10("./dataset",train=False,download=True)
#然后我们运行,来让它下载数据集

#可以看到,很快就下载成功了,然后它在项目中创建了文件夹 dataset

#接下来,让我们来对数据集进行一系列的操作,来读取一下它

#读取一下test_set 的第一个图像
print(test_set[0])
#返回结果:
#(<PIL.Image.Image image mode=RGB size=32x32 at 0x23A4C68C9D0>, 3)
#第一个参数是图像 类型为PIL ,但是第二个参数我们不知道是什么,于是我们来进行一下断点调试
#在控制台处,我们发现第二参数是这个图象对应的 target

#所以,让我们来查看一下这个数据的所有的target 还有 类别 classes
# print(test_set.targets)
print(test_set.classes,1)
#返回结果:
#[3, 8, 8, 0, 6, 6, 1, 6, 3, 1, 0, 9, 5, 7, 9, 8, 5, 7, 8, 6,... 太多了,为了之后调用,我们将其注释掉
#['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']

#然后我们将第一张图片赋值给 img,target,并对其进行输出
img,target = test_set[0] #因为其有两个变量,所以可以这样进行赋值
print(img)
print(target)
#返回结果:
#<PIL.Image.Image image mode=RGB size=32x32 at 0x1A93029B9A0>
#3

#然后我们查看,这一个图像对应的类别 class
print(test_set.classes[target])
#返回结果:
#cat

#之后我们打开这个图像
#img.show() #因为这个图像是PIL 类型的 所以可以直接打开
#我们就可以看见这个猫猫了

###到此为止,我们对数据集的粗略使用就到这

#之后,我们想要将之前学过的transform 和 数据集进行联动
#我们先来自定义transform函数
#直接进行compose化,暂且使用totensor将所有的PIL图片 都转化为 tensor 类型
dataset_transforms = torchvision.transforms.Compose([
torchvision.transforms.ToTensor()
])
#我们之前发现,我们能设置数据集的参数里,有tansforms 和 target_tranforms
#这也就体现了数据集的好处,直接进行 transform处理
train_set = torchvision.datasets.CIFAR10("./dataset",train=True,transform=dataset_transforms,download=True)
test_set = torchvision.datasets.CIFAR10("./dataset",train=False,transform=dataset_transforms,download=True)

#之后,我们来简单验证一下是否转换成功
print(test_set[0])
#返回结果:
#(tensor([[[0.6196, 0.6235, 0.6471, ..., 0.5373, 0.4941, 0.4549],
#可以看到,已经转换成功

#之后,我们就可以利用tensorboard 进行可视化了
#我们首先创建一个用来存放事例的文件夹
writer = SummaryWriter("cifar10")
#然后利用for 循环进行批量写入
for i in range(10): #这里表示 0-9,(从0 开始,range里的参数减一)
img_10,target_10 = test_set[i]
writer.add_image("test",img_10,i)

#一定要记得关闭
writer.close()
#然后运行,在终端查看结果
#tensorboard --logdir=cifar10
###shenxiao
#由此我们就可以看见 0-9 步骤 共10个图像了



#快捷键:
#ctrl + / 进行批量注释

Dataloader的使用

在机器学习中,数据集通常是非常大的,并且可能无法一次性全部载入到内存中。因此,我们需要一种方法来对数据进行分批次载入,这就是数据加载器(DataLoader)的作用。而PyTorch中的DataLoader就是一种常用的数据加载器。

DataLoader可以将数据集分成多个小批次进行载入,并且可以支持多线程异步加载数据,使得数据的读取和训练可以同时进行,提高了训练效率。在每个小批次数据被载入后,我们可以对其进行预处理或者数据增强等操作。

DataLoader通常需要传入一个数据集实例和一些参数,如批次大小、是否打乱数据、使用的线程数量等。使用DataLoader载入数据集时,可以通过for循环来遍历数据集中的每个小批次数据,如:

1
2
3
for images, labels in train_loader:
# 这里可以对images和labels进行操作,如模型训练、预处理等
pass

在使用PyTorch进行深度学习模型训练时,通常会将数据集和DataLoader配合使用,来实现数据的载入和预处理等功能。

类源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
class DataLoader(Generic[T_co]):
r"""
Data loader. Combines a dataset and a sampler, and provides an iterable over
the given dataset.

The :class:`~torch.utils.data.DataLoader` supports both map-style and
iterable-style datasets with single- or multi-process loading, customizing
loading order and optional automatic batching (collation) and memory pinning.

See :py:mod:`torch.utils.data` documentation page for more details.

Args:
dataset (Dataset): dataset from which to load the data.从中加载数据的数据集。
batch_size (int, optional): how many samples per batch to load 每批要装载多少个样品,注意是从样本集里随机抽取
(default: ``1``).
shuffle (bool, optional): set to ``True`` to have the data reshuffled 设置为“True”以重新整理数据,理解为洗牌
at every epoch (default: ``False``).
sampler (Sampler or Iterable, optional): defines the strategy to draw
samples from the dataset. Can be any ``Iterable`` with ``__len__``
implemented. If specified, :attr:`shuffle` must not be specified.
batch_sampler (Sampler or Iterable, optional): like :attr:`sampler`, but
returns a batch of indices at a time. Mutually exclusive with
:attr:`batch_size`, :attr:`shuffle`, :attr:`sampler`,
and :attr:`drop_last`.
num_workers (int, optional): how many subprocesses to use for data 多进程还是单进程
loading. ``0`` means that the data will be loaded in the main process.
(default: ``0``)
collate_fn (Callable, optional): merges a list of samples to form a
mini-batch of Tensor(s). Used when using batched loading from a
map-style dataset.
pin_memory (bool, optional): If ``True``, the data loader will copy Tensors
into device/CUDA pinned memory before returning them. If your data elements
are a custom type, or your :attr:`collate_fn` returns a batch that is a custom type,
see the example below.
drop_last (bool, optional): set to ``True`` to drop the last incomplete batch, 设置为“True”以删除最后一个未完成的批次
if the dataset size is not divisible by the batch size. If ``False`` and
the size of dataset is not divisible by the batch size, then the last batch
will be smaller. (default: ``False``)
timeout (numeric, optional): if positive, the timeout value for collecting a batch
from workers. Should always be non-negative. (default: ``0``)
worker_init_fn (Callable, optional): If not ``None``, this will be called on each
worker subprocess with the worker id (an int in ``[0, num_workers - 1]``) as
input, after seeding and before data loading. (default: ``None``)
generator (torch.Generator, optional): If not ``None``, this RNG will be used
by RandomSampler to generate random indexes and multiprocessing to generate
`base_seed` for workers. (default: ``None``)
prefetch_factor (int, optional, keyword-only arg): Number of batches loaded
in advance by each worker. ``2`` means there will be a total of
2 * num_workers batches prefetched across all workers. (default: ``2``)
persistent_workers (bool, optional): If ``True``, the data loader will not shutdown
the worker processes after a dataset has been consumed once. This allows to
maintain the workers `Dataset` instances alive. (default: ``False``)
pin_memory_device (str, optional): the data loader will copy Tensors
into device pinned memory before returning them if pin_memory is set to true.


.. warning:: If the ``spawn`` start method is used, :attr:`worker_init_fn`
cannot be an unpicklable object, e.g., a lambda function. See
:ref:`multiprocessing-best-practices` on more details related
to multiprocessing in PyTorch.

.. warning:: ``len(dataloader)`` heuristic is based on the length of the sampler used.
When :attr:`dataset` is an :class:`~torch.utils.data.IterableDataset`,
it instead returns an estimate based on ``len(dataset) / batch_size``, with proper
rounding depending on :attr:`drop_last`, regardless of multi-process loading
configurations. This represents the best guess PyTorch can make because PyTorch
trusts user :attr:`dataset` code in correctly handling multi-process
loading to avoid duplicate data.

However, if sharding results in multiple workers having incomplete last batches,
this estimate can still be inaccurate, because (1) an otherwise complete batch can
be broken into multiple ones and (2) more than one batch worth of samples can be
dropped when :attr:`drop_last` is set. Unfortunately, PyTorch can not detect such
cases in general.

See `Dataset Types`_ for more details on these two types of datasets and how
:class:`~torch.utils.data.IterableDataset` interacts with
`Multi-process data loading`_.

.. warning:: See :ref:`reproducibility`, and :ref:`dataloader-workers-random-seed`, and
:ref:`data-loading-randomness` notes for random seed related questions.
"""

PS:在windows下使用num_workers时有时会报BrokenPipError错误,这时我们要把它设置为0

1
2
3
num_workers (int, optional): how many subprocesses to use for data 多进程还是单进程
loading. ``0`` means that the data will be loaded in the main process.
(default: ``0``)

image

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor())
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=True,num_workers=0,drop_last=False)

writer = SummaryWriter("dataloader")
step = 0
for data in test_loader:
imgs,targets = data
writer.add_images("test_dataloader",imgs,step)
step += 1

writer.close()

image

image

但是我们发现最后一张是只有8列两行,如何解决:drop_last=True

设置为True后,则当发现最后一张不足batch_size=64张时它就会舍去。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor())
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=True,num_workers=0,drop_last=True)

writer = SummaryWriter("dataloader")
step = 0
for data in test_loader:
imgs,targets = data
writer.add_images("test_dataloader",imgs,step)
step += 1

writer.close()

shuffle,用 epoch来测试一下shuffle,因为epoch不是1就是0,相当于循环两次

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

test_data = torchvision.datasets.CIFAR10("./dataset",train=False,transform=torchvision.transforms.ToTensor())
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=False,num_workers=0,drop_last=True)

writer = SummaryWriter("dataloader")
step = 0

for epoch in range(2): #这里epoch的值就是0,1 仅仅表示训练两遍,并且配合"Epoch:{}".format(epoch) 实现标识区分
step=0
for data in test_loader:
imgs,targets = data
writer.add_images("Epoch:{}".format(epoch),imgs,step)
step += 1
writer.close()

image

当shuffle设置为false时,输出的两个结果图片的顺序不同,是打乱的,如果shuffle设置为True,则输出的两个结果图片的顺序相同。

PS:epoch如何使用?

在机器学习和深度学习中,”epoch” 是一个术语,表示训练模型时遍历整个训练数据集的一次过程。Python 本身没有内置的 “epoch” 功能,但在使用深度学习框架(如 TensorFlow 或 PyTorch)时,你会经常遇到 “epoch” 的概念。

以下是一个使用 Python 和 PyTorch 的简单示例,展示了如何在训练过程中使用 “epoch”:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
import torch
import torch.nn as nn
import torch.optim as optim
from torch.utils.data import DataLoader
from torchvision import datasets, transforms

# 定义超参数
batch_size = 64
learning_rate = 0.01
num_epochs = 10

# 数据预处理
transform = transforms.Compose([transforms.ToTensor(),
transforms.Normalize((0.1307,), (0.3081,))])

# 加载训练数据集
train_dataset = datasets.MNIST(root='./data',
train=True,
transform=transform,
download=True)
train_loader = DataLoader(train_dataset, batch_size=batch_size, shuffle=True)

# 定义模型、损失函数和优化器
model = nn.Sequential(
nn.Flatten(),
nn.Linear(784, 128),
nn.ReLU(),
nn.Linear(128, 10)
)
loss_function = nn.CrossEntropyLoss()
optimizer = optim.SGD(model.parameters(), lr=learning_rate)

# 训练模型
for epoch in range(num_epochs):
for batch_idx, (data, target) in enumerate(train_loader):
# 前向传播
output = model(data)

# 计算损失
loss = loss_function(output, target)

# 反向传播和优化
optimizer.zero_grad()
loss.backward()
optimizer.step()

# 打印每个 epoch 的损失
print("Epoch [{}/{}], Loss: {:.4f}".format(epoch+1, num_epochs, loss.item()))

在这个示例中,我们使用 PyTorch 框架训练一个简单的神经网络模型。我们在训练循环中设置了一个名为 num_epochs 的变量,用于控制训练的 epoch 数。接下来,我们使用一个外层循环遍历每个 epoch,并在内层循环中遍历训练数据集的每个 batch。在每个 batch 上,我们执行前向传播、计算损失、反向传播和优化步骤。每个 epoch 结束时,我们打印当前 epoch 的损失信息。

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
# _*_coding:utf-8_*_

#首先说一下dataset 和 dataloader 之间的联系
#dataset是整合数据
#dataloader是加载数据,取数据 以便于直接引入到神经网络的训练之中
'''
torch.utils.data.DataLoader(dataset, batch_size=1, shuffle=False,
sampler=None, batch_sampler=None, num_workers=0, collate_fn=None,
pin_memory=False, drop_last=False, timeout=0, worker_init_fn=None,
multiprocessing_context=None, generator=None, *, prefetch_factor=2, persistent_workers=False)[source]
我们可以很明显的看出,这里的参数大部分都有默认值,所以我们暂且挑一点经常使用的进行讲解

Data loader. Combines a dataset and a sampler(采样器), and provides an iterable(可迭代的) over the given dataset.

The DataLoader supports both map-style and iterable-style datasets with single- or multi-process loading,
customizing(自定义) loading order(顺序) and optional automatic batching (collation) and memory pinning(固定).
自定义加载顺序和可选的自动批处理(整理)和内存固定。
See torch.utils.data documentation page for more details.


'''
#现在,我们来使用dataloader
import torchvision
#首先,准备测试数据集
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

test_data = torchvision.datasets.CIFAR10("./dataset",train=True,transform=torchvision.transforms.ToTensor())
# [img,label]
test_loader = DataLoader(dataset=test_data,batch_size=64,shuffle=True,num_workers=0,drop_last=True)
# batch
#测试数据集中的第一张图片及其Target
#我们还要知道 test_data 中的输出是什么,
#法一:
print(test_data[0])
#根据输出可知道是 tensor图像 和 target
#法二:去找CIFAR10源代码中的getitem 的输出
# return img, target
#也可知道输出是 img,target
img,target= test_data[0]
print(img.shape)
print(target)
#返回结果:
# torch.Size([3, 32, 32])
# 6

#而现在我们使用了dataloard batch_size=4 也就是 将四个image变为 images 将这四个image对应的target 变为 targets 然后输出
for data in test_loader:
print(data)
imgs,targets = data
print(imgs.shape)
print(targets)
pass
#返回结果:
# torch.Size([4, 3, 32, 32])
# tensor([7, 9, 1, 6])
# torch.Size([4, 3, 32, 32])
# tensor([5, 6, 5, 3])
# ...
#可见图像已经被分成batch batch_size=4
#通过断点调试我们可以看见:
#sampler(采集器)={RandomSampler: 50000} 也就是说是随机采集的图像 所以这里的tensor 也是随机的四个值

#因为在数据集已经将图像转化为了 tensor型,所以直接进行可视化
writer = SummaryWriter("dataloader_1")
step = 0
for data in test_loader:
imgs,targets = data
# writer.add_image("test_loader",imgs,step)
writer.add_images("test_dataloader",imgs,step)
step += 1

#一定要记得close
writer.close()

#这里出错了
#size of input tensor and input format are different. tensor shape: (4, 3, 32, 32), input_format: CHW
#将写入图片改为写入多个图片
#writer.add_image() -> writer.add_images()

#打开 在tensorboard

#步骤太多了,我们将batch_size改为64,将SummaryWriter 改一下
#卧槽,改成64,创建的事例文件识别不了了
#tensorboard --logdir=dataloard_1 哦哦,吓我一跳 名字抄错了
#可以了

#我们来用 epoch技巧 来玩一下shuffle 首先要把shuffle改为 False
for epoch in range(2): #这里epoch的值就是0,1 仅仅表示训练两遍,并且配合"Epoch:{}".format(epoch) 实现标识
step=0
for data in test_loader:
imgs,targets = data
writer.add_images("Epoch:{}".format(epoch),imgs,step)
step += 1
writer.close()


''' def format(self, *args, **kwargs): # known special case of str.format
"""
S.format(*args, **kwargs) -> str

Return a formatted(格式化) version of S, using substitutions(替换) from args and kwargs.
The substitutions are identified by braces(大括号) ('{' and '}').
"""
比如之上的那个例子,就会像大括号里传入 0 或 1
pass
'''

#来测试shuffle 我们首先将shuffle设为 True
#则Epoch1 和 Epoch2 就会是不一样的
#若将shuffle设为False
#则Epoch1 和 Epoch2 就会是一样的


# 图片在pytorch中用张量表示的规律:
# [batch_size, channel , height, width]

神经网络

官网案例:

image

image

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module): #Model 是模型的名字 (nn.module 是继承这个类 模板)
def __init__(self): #初始化部分
super(Model, self).__init__() #(这是必须要的,调用的初始化函数 表示引用nn.module 进行一个声明)
#后面这些是有我们来设置的一些自定义的数值 要传送到神经网络中的
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 20, 5)

def forward(self, x): #(forword 向前的 表示前向传播网络)
#下面就是网络的运用
x = F.relu(self.conv1(x)) #conv1 第一次卷积 然后 relu 进行一个非线性化操作
return F.relu(self.conv2(x)) #conv2 第二次卷积 然后 relu 进行非线性化操作 然后返回值

神经网络的基本骨架-nn.Module的使用

类源代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
class Module:
r"""Base class for all neural network modules.所有神经网络模块的基类。

Your models should also subclass this class.您的模型还应该将这个类划分为子类。

Modules can also contain other Modules, allowing to nest them in
a tree structure. You can assign the submodules as regular attributes::
模块也可以包含其他模块,允许将它们嵌套在树形结构。您可以将子模块指定为常规属性:

import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module):
def __init__(self):
super().__init__()
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 20, 5)

def forward(self, x):
x = F.relu(self.conv1(x))
return F.relu(self.conv2(x))

Submodules assigned in this way will be registered, and will have their
parameters converted too when you call :meth:`to`, etc.
以这种方式分配的子模块将被注册,并且调用时也转换了参数:

.. note::
As per the example above, an ``__init__()`` call to the parent class
must be made before assignment on the child.
根据上面的例子,对父类的`__init__()``调用必须在分配给孩子之前进行。

:ivar training: Boolean represents whether this module is in training or
evaluation mode.
ivar训练:布尔值表示该模块是否正在训练或评估模式。
:vartype training: bool
"""

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import torch
from torch import nn


class xiaoxiao(nn.Module):
def __init__(self): #进行初始化
super(xiaoxiao, self).__init__() #继承后记得先初始化父类
#我们先不自定义参数

def forward(self,input): #我们来构建前向传播网络 里边放了参数input
output = input + 1
return output

#下面我们来使用创建的神经网络
xiao = xiaoxiao()
x = torch.tensor(1.0)
output = xiao(x)
print(output)

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
# _*_coding:utf-8_*_
#本节我们来学习一下关于神经网络的搭建
#位置在于torch.nn_containers_module
#containers 容器 module 模块
'''
Class torch.nn.module
Base class for all neural network modules.(对于所有的神经网络的一个基础的模块)

Your models should also subclass this class.(继承这个子类)

Modules can also contain other Modules,
allowing to nest(嵌套) them in a tree structure(结构).
You can assign(分配) the submodules as regular attributes(属性):
这是一个例子:
import torch.nn as nn
import torch.nn.functional as F

class Model(nn.Module): Model 是模型的名字 (nn.module 是继承这个类 模板)
def __init__(self): 初始化部分
super(Model, self).__init__() (这是必须要的,调用的初始化函数 表示引用nn.module 进行一个声明)
后面这些是有我们来设置的一些自定义的数值 要传送到神经网络中的
self.conv1 = nn.Conv2d(1, 20, 5)
self.conv2 = nn.Conv2d(20, 20, 5)

def forward(self, x): (forword 向前的 表示前向传播网络)
下面就是网络的运用
x = F.relu(self.conv1(x)) conv1 第一次卷积 然后 relu 进行一个非线性化操作
return F.relu(self.conv2(x)) conv2 第二次卷积 然后 relu 进行非线性化操作 然后返回值

这就是一个简单的,根据nn.module 类 模板,自定义的神经网络Model 的一个例子

'''
#下面,我们来根据前面的例子,设计我们的第一个神经网络
#以此来熟悉一下 nn.module 的使用方法
#首先,根据类 模板,自定义自己的神经网络
import torch
from torch import nn


class xiaoxiao(nn.Module): #注意,要大写Module
def __init__(self): #进行初始化
super(xiaoxiao, self).__init__() #除了根据提示来写,还可以根据,左上角代码,选择代码补全,以此来进行操作
#不是很好用,还是直接来用提示吧
#我们先不自定义参数
def forward(self,input): #我们来构建前向传播网络 里边放了参数input #注意拼写问题 forward
output = input + 1
return output
#下面我们来使用创建的神经网络
xiao = xiaoxiao()
x = torch.tensor(1.0)
output = xiao(x)
print(output)

#返回结果:
#tensor(2.)

#我们可以debug 一下,看一下代码运行顺序
#在debug里使用(单步执行我的代码)




卷积操作

image

官方文档

torch.nn.functional.conv2d(input, weight, bias=None, stride=1, padding=0, dilation=1, groups=1) → Tensor

Applies a 2D convolution over an input image composed of several input planes.

This operator supports TensorFloat32.

See Conv2d for details and output shape.

NOTE:

In some circumstances when given tensors on a CUDA device and using CuDNN, this operator may select a nondeterministic algorithm to increase performance. If this is undesirable, you can try to make the operation deterministic (potentially at a performance cost) by setting torch.backends.cudnn.deterministic = True. See Reproducibility for more information.

NOTE:

This operator supports complex data types i.e. complex32, complex64, complex128.

  • Parameters:

    input – 提供一个输入(batch,输入通道数量,高,宽)PS:例如彩色图像的通道数就是3

    weight – 权重(卷积核)(输出通道数量,输入通道数量/group,高,宽)

    bias – 偏置

    stride – 卷积核的步长

    padding – 在输入的周围进行填充

    dilation

    groups

image

示例

image

给出的代码片段创建了一个二维输入矩阵(表示为 PyTorch 张量)和一个二维卷积核。然后,将输入矩阵和卷积核重塑为具有适当尺寸的四维张量,以便将它们用于卷积操作。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
import torch

# 二维矩阵
input = torch.tensor([[1, 2, 0, 3, 1],
[0, 1, 2, 3, 1],
[1, 2, 1, 0, 0],
[5, 2, 3, 1, 1],
[2, 1, 0, 1, 1]])

kernel = torch.tensor([[1, 2, 1],
[0, 1, 0],
[2, 1, 0]])
print(kernel)
print(kernel.shape) #发现输出的是一个2D张量,需要转换成一个4D张量

# 将输入矩阵和卷积核重塑为 4D 张量,因为conv2d的第一个参数是,input – 提供一个输入(batch,输入通道,高,宽)
#用reshape 进行转换
input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))

print(kernel)
print(kernel.shape)
1
2
3
4
5
6
7
8
9
10
#输出结果

tensor([[1, 2, 1],
[0, 1, 0],
[2, 1, 0]])
torch.Size([3, 3])
tensor([[[[1, 2, 1],
[0, 1, 0],
[2, 1, 0]]]])
torch.Size([1, 1, 3, 3])

现在,可以使用 torch.nn.functional.conv2d() 函数将卷积核应用于输入矩阵。这是如何实现的:

1
2
3
4
5
6
import torch.nn.functional as F

# 进行卷积操作
output = F.conv2d(input, kernel,stride=1)
print(output)
print(output.shape)

请注意,卷积操作后的输出尺寸将根据输入矩阵、卷积核大小以及您使用的填充(padding)和步长(stride)而有所不同。默认情况下,torch.nn.functional.conv2d() 使用填充 0 和步长 1,这将生成一个较小的输出矩阵。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import torch
import torch.nn.functional as F

input = torch.tensor([[1, 2, 0, 3, 1],
[0, 1, 2, 3, 1],
[1, 2, 1, 0, 0],
[5, 2, 3, 1, 1],
[2, 1, 0, 1, 1]])

kernel = torch.tensor([[1, 2, 1],
[0, 1, 0],
[2, 1, 0]])

input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))

output = F.conv2d(input, kernel,stride=1)
print(output)
print(output.shape)
1
2
3
4
5
6
#输出结果

tensor([[[[10, 12, 12],
[18, 16, 16],
[13, 9, 3]]]])
torch.Size([1, 1, 3, 3])

可以使用padding在输入的矩阵周围进行填充,空的地方默认是填充为0的:

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
import torch
import torch.nn.functional as F

input = torch.tensor([[1, 2, 0, 3, 1],
[0, 1, 2, 3, 1],
[1, 2, 1, 0, 0],
[5, 2, 3, 1, 1],
[2, 1, 0, 1, 1]])

kernel = torch.tensor([[1, 2, 1],
[0, 1, 0],
[2, 1, 0]])

input = torch.reshape(input, (1, 1, 5, 5))
kernel = torch.reshape(kernel, (1, 1, 3, 3))

output = F.conv2d(input, kernel,stride=1,padding=1)
print(output)
1
2
3
4
5
tensor([[[[ 1,  3,  4, 10,  8],
[ 5, 10, 12, 12, 6],
[ 7, 18, 16, 16, 8],
[11, 13, 9, 3, 4],
[14, 13, 9, 7, 4]]]])

在这段代码中,padding 参数用于设置卷积操作中的填充。填充是指在输入矩阵的边缘添加一定数量的零值行和列。这样做有以下几个目的:

  1. 保持空间尺寸:填充可以帮助在卷积操作后保持输入矩阵的空间尺寸。这在构建深度卷积神经网络时尤为重要,因为可以防止尺寸在网络的多个层之间过快收缩。
  2. 边缘信息处理:填充可以帮助在卷积操作中更好地处理边缘信息,因为卷积核可以完全覆盖输入矩阵的边缘像素。

在您的代码示例中,padding=1 表示在输入矩阵的四个边缘都添加一层零值行和列。这样,在应用 3x3 的卷积核时,输出矩阵的尺寸将保持与输入矩阵相同(5x5)。

PS:当 padding 设置为 2 时,将在输入矩阵的四个边缘都添加两层零值行和列。

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
#本节我们来学习卷积操作 convolution
#首先来介绍一下torch.nn 和 torch.nn.functinal 的区别
#这是已经整合好的 相当于方向盘
#这是没有整合的,相当于一个个的齿轮
#我们讲的是 torch.nn_convolutinal layers
#nn.Conv1d 表示是一维的
#nn.Conv2d 表示是二维的 我们主要利用这个
'''
Conv2d

class
torch.nn.Conv2d(in_channels, out_channels, kernel_size,
stride=1, padding=0, dilation=1, groups=1, bias=True, padding_mode='zeros',
device=None, dtype=None)

Applies a 2D convolution over an input signal 信号 composed of 多个 several input planes 平面.



stride 步 卷积核移动的长度 controls the stride for the cross-correlation, a single number or a tuple.

padding 填充 controls the amount of padding applied to the input.
It can be either a string {‘valid’, ‘same’} or a tuple
of ints giving the amount of implicit padding applied on both sides. 填充一般为0

dilation controls the spacing between the kernel points;
also known as the à trous algorithm.
It is harder to describe, but this link has a nice visualization of what dilation does.

groups controls the connections between inputs and outputs.
in_channels and out_channels must both be divisible by groups. For example,

At groups=1, all inputs are convolved to all outputs.

At groups=2, the operation becomes equivalent to having two conv layers side by side,
each seeing half the input channels and producing half the output channels,
and both subsequently concatenated.

Parameters

in_channels (int) – Number of channels in the input image

out_channels (int) – Number of channels produced by the convolution

kernel_size (int or tuple) – Size of the convolving kernel 内核
卷积核

stride (int or tuple, optional) – Stride of the convolution. Default: 1
移动步长
padding (int, tuple or str, optional) – Padding added to all four sides of the input.
Default: 0
填充
padding_mode (string, optional) – 'zeros', 'reflect', 'replicate' or 'circular'.
Default: 'zeros'

dilation (int or tuple, optional) – Spacing between kernel elements. Default: 1

groups (int, optional) – Number of blocked connections from input channels to output channels.
Default: 1

bias (bool, optional) – If True, adds a learnable bias to the output. Default: True


'''

#下面我们来实际使用一下卷积操作】
#首先输入要进行卷积的tensor 张量
import torch
import torch.nn.functional as F

input = torch.tensor([[1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]]) #] 的数量就表示这个张量是几维的
#确定卷积核
kernel = torch.tensor([[1,2,1],
[0,1,0],
[2,1,0]])
print(kernel)
#我们要先来看一下,我们设置的这两个张量的尺寸
# print(input.shape)
print(kernel.shape)
#返回结果:
# torch.Size([4, 5])
# torch.Size([3, 3])
#我们看一下 conv2 要求我们输入的尺寸

'''
Input: (N,Cin,Hin,Win)(N, C_{in}, H_{in}, W_{in})(N,Cin​,Hin​,Win​)
明显有四个值 我们的不符合要求 分别要加上 minibatch 批的大小 in_channels 输入的通道大小
'''
#我们用reshape 进行转换
input = torch.reshape(input,(1,1,5,5))
kernel = torch.reshape(kernel,(1,1,3,3))
print(kernel)

#转换之后看一下shape
# print(input.shape)
print(kernel.shape)
#返回结果:
# torch.Size([1, 1, 5, 5])
# torch.Size([1, 1, 3, 3])
kernel1 = torch.reshape(kernel,(3,1,1,3))
print(kernel1)
print(kernel1.shape)
#转换完形状了,我们就来进行卷积操作
output = F.conv2d(input,kernel,stride=1)
print(output)
# 2d convolution

output_2 = F.conv2d(input,kernel,stride=2)
print(output_2)

# input 被操作的对象
# weight 权重 放的是卷积核
# stride 步长 stride=1
# padding 填充 padding=1
# output 输出 结果


#返回结果:
# tensor([[[[10, 12, 12],
# [18, 16, 16],
# [13, 9, 3]]]])
# tensor([[[[10, 12],
# [13, 3]]]])

#下面,我们加上padding 填充操作

output_3 = F.conv2d(input,kernel,stride=1,padding=1)
print(output_3)
#返回结果:
# tensor([[[[ 1, 3, 4, 10, 8],
# [ 5, 10, 12, 12, 6],
# [ 7, 18, 16, 16, 8],
# [11, 13, 9, 3, 4],
# [14, 13, 9, 7, 4]]]])

# input: W * H
# W(width 宽度) 5 H 5

# weight : w * h
# w 3 h 3

# padding 0

# stride 1


# W1 = (5 + 2*0 -3) / 1 + 1 = 3
# H1 = (5 + 2*0 -3) / 1 + 1 = 3

# oupput: W1 * H2

# 卷积图示:
# https://blog.csdn.net/kingroc/article/details/88192878

# 卷积公式:
# https://blog.csdn.net/qq_40894813/article/details/119008600





神经网络-卷积层

官方文档

https://pytorch.org/docs/stable/generated/torch.nn.Conv2d.html#torch.nn.Conv2d

image

image

image

PS:in_channle=1,out_channle=2的情况:

image

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
import torch
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

# 加载 CIFAR-10 数据集
dataset = torchvision.datasets.CIFAR10("./conv", train=False, transform=torchvision.transforms.ToTensor(), download=True)

# 创建数据加载器,批量大小为 64
dataloader = DataLoader(dataset, batch_size=64)

# 定义一个简单的卷积神经网络
class xiao(nn.Module):
def __init__(self):
super(xiao, self).__init__()
self.conv1 = Conv2d(3, 6, 3, 1, 0)
# 3 个输入通道(彩色图像),6 个输出通道,3x3 的卷积核,步长为 1,填充为 0

def forward(self, input):
input = self.conv1(input) # 进行卷积操作
return input

xiaoxiao = xiao() # 初始化网络
writer = SummaryWriter("conv_1") # 用于 TensorBoard 可视化的摘要编写器
step = 0

# 对数据集中的每个批次执行以下操作
for data in dataloader:
imgs, targets = data # 从数据加载器中获取图像和标签
output = xiaoxiao(imgs) # 将图像传递给网络并获取输出

# 将原始图像写入 TensorBoard
writer.add_images("conv_0", imgs, global_step=step)

# 调整网络输出的形状,因为6个通道的它无法显示,会报错,所以这里用了一个不严谨的方法,reshape一下,以便将其写入 TensorBoard
output = torch.reshape(output, (-1, 3, 30, 30))#当地一个参数不知道是多少时写-1,第二个参数改为3,第三个和第四个为30×30的图片
writer.add_images("conv_1", output, global_step=step)

step += 1

writer.close() # 关闭摘要编写器

这段代码定义了一个简单的卷积神经网络(xiao 类),用于处理 CIFAR-10 数据集中的图像。卷积网络只有一个卷积层,接受 3 个输入通道(彩色图像),输出 6 个通道,使用 3x3 的卷积核,步长为 1,填充为 0。

代码使用 DataLoader 加载 CIFAR-10 数据集,批量大小为 64。然后,它遍历数据集中的每个批次,将图像传递给卷积神经网络,并将原始图像和卷积操作后的图像写入 TensorBoard,以便进行可视化。

image

PS:通道数举例

image

这个图片的前两部分发现输入的in_channle是3,out_channle是64

PS:本案例中3x3 的卷积核的作用

卷积核是在卷积神经网络中用于执行卷积操作的一个小矩阵。3x3 的卷积核表示这个矩阵的尺寸是 3 行 3 列。卷积核在图像处理和深度学习中有着广泛的应用,主要用于提取图像特征和降低图像噪声。

卷积操作将卷积核应用于输入数据(通常是图像),卷积核以滑动窗口的方式在输入数据上移动。在每个位置,卷积核与其覆盖的输入数据进行逐元素乘法,然后将结果相加得到输出数据(通常称为特征图)的对应位置的值。

在深度学习中,卷积核的参数(矩阵中的每个值)是通过训练过程学习得到的。通过优化这些参数,卷积神经网络能够学习检测图像中的各种特征,例如边缘、纹理和更高层次的抽象特征。

3x3 的卷积核具有以下作用:

  1. 局部连接:卷积核只关注输入图像的一个小局部区域,这使得网络能够专注于提取局部特征,同时减少了计算复杂度。
  2. 参数共享:卷积核在整个输入图像上滑动时,使用相同的参数。这减少了模型的参数数量,降低了过拟合的风险,并提高了计算效率。
  3. 平移不变性:由于参数共享,卷积神经网络具有平移不变性,即无论特征出现在图像的哪个位置,网络都可以有效地检测到。

在卷积神经网络中,可以使用不同大小的卷积核。3x3 的卷积核通常被认为是一个很好的折衷方案,因为它能够在保持较低计算复杂度的同时,提取有足够代表性的特征。然而,根据应用场景和问题领域,其他尺寸的卷积核(如 1x1、5x5 或 7x7)也可能是合适的选择。

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
# _*_coding:utf-8_*_
#本节我们学习在数据集上实现convolution layers
#并复习一下上一节课讲的卷积操作,以及对于数据集的使用
import torch
#首先引入数据集
import torchvision
from torch import nn
from torch.nn import Conv2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)
#注意,这里加一个点就行,加多了找不到创建的文件在哪里
#其次加载数据集

dataloader = DataLoader(dataset,batch_size=64)

#创建神经网络类
class xiao(nn.Module): #继承nn.Module 类
def __init__(self):
super(xiao, self).__init__()
#创建卷积操作
self.conv1 = Conv2d(in_channels=3,out_channels=6,kernel_size=3,stride=1,padding=0)
#数据集中的图像是RGB 我们想让它输出6通道的
#卷积核自动生成 (3,3) 随机生成的???
def forward(self,input):
input = self.conv1(input) #进行卷积操作
return input
#现在来使用卷积网络
xiaoxiao = xiao() #初始化网络
#输出网络的结果来看一看
# print(xiaoxiao)
#返回结果:
# xiao(
# (conv1): Conv2d(3, 6, kernel_size=(3, 3), stride=(1, 1))
# )
writer = SummaryWriter("conv_1")
step = 0
#先注释掉,然后我们来对数据集使用神经网络
for data in dataloader: #首先得加载数据集 到 data
imgs,targets = data
output = xiaoxiao(imgs) #使用神经网络
#print(imgs.shape) #来查看一下数据集过了神经网络之后的结果
# torch.Size([64, 3, 32, 32])
#print(output.shape)
# torch.Size([64, 6, 30, 30])
writer.add_images("conv_0",imgs,global_step=step)
output = torch.reshape(output,(-1,3,30,30))
writer.add_images("conv_1",output,global_step=step)

step += 1
#返回结果:
# torch.Size([64, 3, 32, 32])
# torch.Size([64, 6, 30, 30]) #因为做了卷积操作 所以 H W 变小了
# ...
#可以看到转换成功了

writer.close()

#然后我们用tensorboard继续可视化处理
#首先需要创建文件夹来存放事例文件
#写入图像
#放在上面了

#然后运行
#又忘了close了!!!

#报错了:
# assert I.ndim == 4 and I.shape[1] == 3
#图像显示是3个channel 我们给出的是6个channel 它不知道如何显示

#这里我们用一个 不太严谨的方法,对图像进行一个转换
# output = torch.reshape(output,(-1,3,30,30))
#注意,这里取-1 的意思是 batch_size 让它自己算完填进去

#再次运行
#在终端进行打开

#我们可以注意到 conv_1 的一个batch 是2*64 这是重新计算得出的 根据channel 除以了 2 也可以理解 所以 batch_size 乘以2

神经网络-最大池化的使用

什么是池化

池化(Pooling)是卷积神经网络(CNN)中的一种操作,用于减小特征图的尺寸,同时保留重要的空间信息。池化操作可以减少网络的参数数量,降低计算复杂度,提高计算效率,并有助于减轻过拟合现象。

池化操作的基本原理是在特征图的局部区域上进行降采样。这些局部区域通常不重叠,且具有固定的大小(例如 2x2 或 3x3)。池化操作在每个局部区域上应用一个聚合函数(如最大值、平均值等),然后将结果组合成一个新的、更小的特征图。

池化的主要类型有:

  1. 最大池化(Max Pooling):在每个局部区域上选择最大值作为输出。这是最常用的池化方法,因为它能够保留特征图中的最显著特征。
  2. 平均池化(Average Pooling):在每个局部区域上计算平均值作为输出。平均池化能够保留更多的背景信息,但可能在提取显著特征方面不如最大池化。
  3. 全局池化(Global Pooling):全局池化将整个特征图的最大值(全局最大池化)或平均值(全局平均池化)作为输出。这种方法通常用于卷积神经网络的最后一层,以替代全连接层,减少参数数量并降低过拟合风险。

池化层通常在卷积层之后添加,以实现特征降维和空间不变性。在现代深度学习框架中(如 PyTorch 和 TensorFlow),池化操作很容易实现,通常只需添加一个预定义的池化层即可。

官方文档

https://pytorch.org/docs/stable/generated/torch.nn.MaxPool2d.html#torch.nn.MaxPool2d

image

image

image

PS:最大池化

最大池化(Max Pooling)是一种池化操作,它在特征图的局部区域上选择最大值作为输出。最大池化有助于减小特征图的尺寸,减少计算复杂度,同时保留最显著的特征。下面是最大池化操作的详细步骤:

  1. 确定池化窗口的大小,例如 2x2 或 3x3。这个窗口将在特征图上滑动以执行池化操作。
  2. 确定池化操作的步长。步长是窗口在特征图上移动的距离。通常,步长与池化窗口的大小相同,以避免重叠区域。
  3. 将池化窗口放置在特征图的左上角。在窗口覆盖的区域内,找到最大值。
  4. 将找到的最大值作为输出特征图的一个元素。
  5. 按照步长移动池化窗口,重复步骤3和4。在特征图的所有位置上执行此操作,直到到达特征图的右下角。
  6. 最终得到的输出特征图是原始特征图经过最大池化操作后的降采样结果。

以下是一个简单的示例。给定一个 4x4 的特征图:

1
2
3
4
Copy code1  3  2  4
5 6 7 8
9 3 1 7
4 0 2 6

应用 2x2 的最大池化操作,步长为 2:

1
2
3
4
5
1 3 | 2 4
5 6 | 7 8
----|----
9 3 | 1 7
4 0 | 2 6

池化窗口将特征图划分为四个不重叠的 2x2 区域。在每个区域中,选取最大值:

1
2
6  8
9 7

最终得到一个 2x2 的输出特征图。可以看到,原始特征图的尺寸已经减小,同时保留了最显著的特征。

示例

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
import torch
from torch import nn
from torch.nn import MaxPool2d

input = torch.tensor([[1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]],dtype=torch.float32)

print(input.shape)

input = torch.reshape(input,(-1,1,5,5)) #这里的batch_size仍然设置为-1 让它计算完自己填进去

class xiaoxiao(nn.Module): #继承一下 nn.Module 类
def __init__(self):
super(xiaoxiao, self).__init__()
self.maxpool_1 = MaxPool2d(kernel_size=3,ceil_mode=True) #ceil_mode=True选择保留不满足kernel大小的
self.maxpool_2 = MaxPool2d(kernel_size=3,ceil_mode=False) #ceil_mode=False选择舍弃不满足kernel大小的部分卷积操作

def forward(self,input):
output_1 = self.maxpool_1(input)
output_2 = self.maxpool_2(input)
return output_1,output_2

xiao = xiaoxiao()
output_1,output_2 = xiao(input)
print(output_1)
print(output_2)

1
2
3
4
5
torch.Size([5, 5])
tensor([[[[2., 3.],
[5., 1.]]]])
tensor([[[[2.]]]])

PS:图形化直观感受一下

image

PS:dtype=torch.float32

在这段代码中,dtype=torch.float32 是用于指定创建张量时的数据类型。在 PyTorch 中,张量可以有不同的数据类型,例如整数、浮点数等。torch.float32 表示 32 位浮点数,是 PyTorch 中的一种常用数据类型。

在这个例子中,dtype=torch.float32 用于将输入张量的数据类型设置为 32 位浮点数。这样做的原因是,神经网络通常处理浮点数数据,因为这种数据类型可以表示更广泛的值范围,并且在许多计算中具有更高的精度。特别是在 PyTorch 中,神经网络操作(如卷积、池化等)通常需要浮点数类型的输入。

这里是创建输入张量时使用 dtype=torch.float32 的代码:

1
2
3
4
5
input = torch.tensor([[1, 2, 0, 3, 1],
[0, 1, 2, 3, 1],
[1, 2, 1, 0, 0],
[5, 2, 3, 1, 1],
[2, 1, 0, 1, 1]], dtype=torch.float32)

如果不指定 dtype=torch.float32,那么 PyTorch 会根据输入数据自动推断数据类型。在这个例子中,输入数据是整数,所以自动推断的数据类型将是 torch.int64。这可能会导致后续神经网络操作出现错误或不兼容。通过显式指定 dtype=torch.float32,可以确保输入张量的数据类型与神经网络操作兼容。

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
# _*_coding:utf-8_*_
#本节我们来学习池化层 Pooling Layers
#第一部分主要学习一下池化层的概念,并且进行一下简单的操作
#池化层:方式有很多 常用的方式有
#nn.maxPool2d 意思是最大池化 向下进行操作 处理数据的位数是2 维张量 tensor
#nn.maxUnpoool2d 也是最大池化 但是是向上进行池化 处理数据同样是 2维张量 tensor
#对于池化的理解:
#相当于将1080p的视频 降维到 720p 使得视频容量大大减小 又不太影响视频对象的识别
#对于图像也就是,在尽可能保留图像特征的同时 降低图像的暂用内存 这是十分必要的一步 要不然内存太大是训练不出来的
#而且也可能参数过多 影响因素过多 造成严重的过拟合
###这里就存在一个假设,即池化的方式保留了需要的特征
#所以有了不同的池化手段嘛 针对不同的数据
#所以这里也就有了一个创新的点 也就是创造新的更加牛逼的池化方式
#相应的,这里的kernel 也就转变为了 池化核
#但本质上还是做的卷积操作 只不过这里的步长 stride 变大了 一般设为核的大小

'''
MaxPool2d

class
torch.nn.MaxPool2d(kernel_size, stride=None,
padding=0, dilation=1, return_indices=False, ceil_mode=False)


Parameters

kernel_size – the size of the window to take a max over

stride – the stride of the window. Default value is kernel_size
默认步长为核的大小
padding – implicit zero padding to be added on both sides

dilation – a parameter that controls the stride of elements in the window
棋盘卷积操作 也就是 四周都隔一个数值进行操作 (很少会用到)
return_indices – if True, will return the max indices along with the outputs.
Useful for torch.nn.MaxUnpool2d later

ceil_mode – when True, will use ceil instead of floor to compute the output shape
Ceiling 和 Floor 相当对应
ceil 天花板 在小数取舍的时候 表示向上取整 到相对的 天花板,而在这里表示的意思是,若不满足核的大小,也保留一个数值
floor 地板 在小数取舍的时候 表示向下取整 到相对的 地板,而在这里表示的是,若不满足核的大小,就直接舍弃,不取值
下面有具体的例子的讲解

'''

#下面,我们具体使用一下池化的操作
import torch
from torch import nn
from torch.nn import MaxPool2d

input = torch.tensor([[1,2,0,3,1],
[0,1,2,3,1],
[1,2,1,0,0],
[5,2,3,1,1],
[2,1,0,1,1]],dtype=torch.float32)

print(input.shape)

#返回结果:
# torch.Size([5, 5])
#我们要找一下,MaxPool2d 输入的要求
#Input: (N,C,Hin,Win)(N, C, H_{in}, W_{in})(N,C,Hin​,Win​) or (C,Hin,Win)(C, H_{in}, W_{in})(C,Hin​,Win​)
#很明显这是要求四个或三个 我们这里还是采用之前学到的技巧 reshape 成 四个参数的
input = torch.reshape(input,(-1,1,5,5)) #这里的batch_size仍然设置为-1 让它计算完自己填进去
print(input.shape)
#返回结果:
# torch.Size([1, 1, 5, 5])
#符合要求了,计算得到的 batch_size 为4

#之上,我们已经构建了好了输入的数据
#下面,我们自定义 类 来创建神经网络
class xiaoxiao(nn.Module): #继承一下 nn.Module 类
def __init__(self):
super(xiaoxiao, self).__init__()
self.maxpool_1 = MaxPool2d(kernel_size=3,ceil_mode=True) #选择保留不满足kernel大小的
self.maxpool_2 = MaxPool2d(kernel_size=3,ceil_mode=False) #选择舍弃不满足kernel大小的部分卷积操作
def forward(self,input):
output_1 = self.maxpool_1(input)
output_2 = self.maxpool_2(input)
return output_1,output_2

#来使用创建的神经网络
xiao = xiaoxiao()
output_1,output_2 = xiao(input)
print(output_1)
print(output_2)

#报错了:
# RuntimeError: "max_pool2d_with_indices_cpu" not implemented for 'Long'
#将tensor数据类型转换为 float32 使用dtype=torch.float32

#输出结果:
# tensor([[[[2., 3.],
# [5., 1.]]]])
# tensor([[[[2.]]]])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
# _*_coding:utf-8_*_
#本节我们学习Pooling Layers
#本部分,我们对数据集进行池化操作
import torchvision
#引入数据集
from torch import nn
from torch.nn import MaxPool2d
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)
#加载数据集
dataloader = DataLoader(dataset,batch_size=64)

#创建神经网络
class xiaoxiao(nn.Module): #要记得继承 nn.Module 类
def __init__(self):
super(xiaoxiao, self).__init__()
self.maxpool = MaxPool2d(kernel_size=3,ceil_mode=False)
def forward(self,input):
output = self.maxpool(input)
return output
#使用神经网络
xiao = xiaoxiao()
#同时想做一个可视化处理
writer = SummaryWriter("pool")
step = 0
#加载数据
for data in dataloader:
imgs,targets=data
#写入未操作的图像
writer.add_images("unchanged",imgs,global_step=step)
output = xiao(imgs)
writer.add_images("pooled",output,global_step=step)
step += 1
#close
writer.close()

#报错了:
#'xiaoxiao' object is not callable

#检查原因;
#自定义的 xiaoxiao() 函数没有继承 nn.Module 类

#在终端运行后查看

#我们可以看出,pooled之后,图像的清晰度下降了,但是我们还是差不多可以识别图像,
#也就是这一种池化操作对这一个数据集,减小了内存大小,减少了噪音特征,大部分保存了有用特征
#可能如此

神经网络-非线性激活

非线性激活函数是在神经网络中应用于神经元输出的函数,用于引入非线性特性。这种非线性特性使得神经网络能够学习和表示更复杂的模式和函数。如果没有非线性激活函数,神经网络将只能表示线性关系,大大限制了其表示能力和学习能力。

非线性激活函数有很多种,以下是一些常用的非线性激活函数:

  1. ReLU(Rectified Linear Unit):ReLU 函数是目前最常用的激活函数之一。它的计算公式为:f(x) = max(0, x)。也就是说,如果输入值 x 为正数,函数值就是 x;如果输入值 x 为负数或零,函数值为 0。ReLU 函数的优点是计算简单且收敛速度快。
  2. Sigmoid:Sigmoid 函数将输入值压缩到 0 到 1 之间。它的计算公式为:f(x) = 1 / (1 + exp(-x))。Sigmoid 函数曾经非常流行,但现在已经被 ReLU 及其变体所取代,因为 Sigmoid 函数在输入值较大或较小时容易出现梯度消失的问题。
  3. Tanh:Tanh 函数是 Sigmoid 函数的双曲正切版本,它将输入值压缩到 -1 到 1 之间。计算公式为:f(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))。与 Sigmoid 函数相比,Tanh 函数在零点附近的梯度更大,因此有时更受欢迎。
  4. Leaky ReLU:Leaky ReLU 是 ReLU 函数的一个变体,其主要目的是解决 ReLU 函数在负数输入时梯度为零的问题。Leaky ReLU 的计算公式为:f(x) = max(alpha * x, x),其中 alpha 是一个很小的正数,通常为 0.01。这样,当输入值 x 为负数时,函数仍然具有非零的梯度。

非线性激活函数在神经网络中扮演着至关重要的角色,它们使得网络能够学习并表示复杂的、非线性的关系。在实际应用中,根据问题的性质和需求,可以选择不同的非线性激活函数来优化神经网络的性能。

PS:什么是线性激活,什么是非线性激活,这两者区别

非线性激活函数是在神经网络中应用于神经元输出的函数,用于引入非线性特性。这种非线性特性使得神经网络能够学习和表示更复杂的模式和函数。如果没有非线性激活函数,神经网络将只能表示线性关系,大大限制了其表示能力和学习能力。

非线性激活函数有很多种,以下是一些常用的非线性激活函数:

  1. ReLU(Rectified Linear Unit):ReLU 函数是目前最常用的激活函数之一。它的计算公式为:f(x) = max(0, x)。也就是说,如果输入值 x 为正数,函数值就是 x;如果输入值 x 为负数或零,函数值为 0。ReLU 函数的优点是计算简单且收敛速度快。
  2. Sigmoid:Sigmoid 函数将输入值压缩到 0 到 1 之间。它的计算公式为:f(x) = 1 / (1 + exp(-x))。Sigmoid 函数曾经非常流行,但现在已经被 ReLU 及其变体所取代,因为 Sigmoid 函数在输入值较大或较小时容易出现梯度消失的问题。
  3. Tanh:Tanh 函数是 Sigmoid 函数的双曲正切版本,它将输入值压缩到 -1 到 1 之间。计算公式为:f(x) = (exp(x) - exp(-x)) / (exp(x) + exp(-x))。与 Sigmoid 函数相比,Tanh 函数在零点附近的梯度更大,因此有时更受欢迎。
  4. Leaky ReLU:Leaky ReLU 是 ReLU 函数的一个变体,其主要目的是解决 ReLU 函数在负数输入时梯度为零的问题。Leaky ReLU 的计算公式为:f(x) = max(alpha * x, x),其中 alpha 是一个很小的正数,通常为 0.01。这样,当输入值 x 为负数时,函数仍然具有非零的梯度。

非线性激活函数在神经网络中扮演着至关重要的角色,它们使得网络能够学习并表示复杂的、非线性的关系。在实际应用中,根据问题的性质和需求,可以选择不同的非线性激活函数来优化神经网络的性能。

官方文档

https://pytorch.org/docs/stable/generated/torch.nn.ReLU.html#torch.nn.ReLU

image

常用:RELU

image

image

常用:Sigmoid

image

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import torch
from torch import nn
#输入数据
from torch.nn import ReLU

input = torch.tensor([[1,-0.5],
[-1,3]])
input = torch.reshape(input,(-1,1,2,2))
#print(input.shape) #输出:torch.Size([1, 1, 2, 2]),发现格式满足要求了

#创建神经网络
class xiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiao, self).__init__()
self.relu1 = ReLU()
def forward(self,input):
output = self.relu1(input)
return output

#使用神经网络
xiao = xiaoxiao()

output = xiao(input)
print(output)

这个代码片段首先导入了所需的库,并创建了一个简单的输入数据。接着,定义了一个名为 xiaoxiao 的神经网络类,该类继承了 PyTorch 中的 nn.Module 类。在这个神经网络类中,定义了一个 ReLU 激活函数。在网络的前向传播过程中,输入数据经过 ReLU 激活函数。

输入数据 input 是一个 2x2 的张量,包含正数和负数。通过 torch.reshape 函数,将其调整为大小为 [1, 1, 2, 2] 的张量,以便在神经网络中处理。这表示批量大小为 1,通道数为 1。

接下来,实例化 xiaoxiao 类并将调整后的输入数据传递给神经网络。ReLU 激活函数将输入数据中的负数变为 0,正数保持不变。

输出结果如下:

1
2
tensor([[[[1., 0.],
[0., 3.]]]])

可以看到,经过 ReLU 激活函数后,负数被转换为 0,而正数保持不变。

PS:那这个程序是怎么调用的?为什么xiao = xiaoxiao()之后再使用output = xiao(input)就会调用def forward(self,input)这个函数了呢?

当你创建一个自定义神经网络类并继承 PyTorch 的 nn.Module 时,这个类会自动获得 nn.Module 的所有属性和方法。在这个例子中,xiaoxiao 类继承了 nn.Module。因此,当你实例化 xiaoxiao 类(xiao = xiaoxiao()),xiao 对象就具有了 nn.Module 类的所有属性和方法。

当你在代码中使用 output = xiao(input) 时,实际上你在调用 nn.Module 类的 __call__ 方法。__call__ 是一个特殊的 Python 方法,当一个对象被当作函数调用时,会自动执行这个方法。在 nn.Module 类中,__call__ 方法会自动调用你重写的 forward 方法。

因此,在这个例子中,当你执行 output = xiao(input) 时,实际上是在调用 nn.Module 类的 __call__ 方法,它进而调用你为 xiaoxiao 类定义的 forward 方法。这就是为什么执行 output = xiao(input) 会触发 forward 方法的原因。

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
# _*_coding:utf-8_*_
#本节我们来学习非线性激活 Non-linear Activations (weighted sum, nonlinearity)
#跳过了Padding Layers 不讲,这个不常用,而且padding 填充操作 可以在卷积层操作中实现 Conv2d

#本部分主要讲非线性激活,及其简单操作

#非线性激活的含义:让数据具有非线性的特性,让函数可以更好的拟合,而不是直直的,导致模型能力不够,然后很难训练

#常用的非线性激活函数:
#ReLu
#Sigmoid

'''

class
torch.nn.ReLU(inplace=False)[source]

Applies 应用 the rectified linear 线性整流 unit function 单位函数 element-wise 逐元素:

Parameters

inplace – can optionally do the operation in-place. Default: False
直接代换,一般设置为 False

input = -1 input = -1
Relu(input.inplace=True) Relu(input.inplace=False)
input = 0 input = -1
output = 0
Shape:

Input: (∗)(*)(∗), where ∗*∗ means any number of dimensions. 很自由 没啥要求

Output: (∗)(*)(∗), same shape as the input.


'''

#下面,我们来具体操作非线性激活函数
import torch
from torch import nn
#输入数据
from torch.nn import ReLU

input = torch.tensor([[1,-0.5],
[-1,3]])
input = torch.reshape(input,(-1,1,2,2)) #为了方便可能以后会用到的函数 来reshape 一下
print(input.shape)
#torch.Size([1, 1, 2, 2])

#创建神经网络
class xiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiao, self).__init__()
self.relu1 = ReLU()
def forward(self,input):
output = self.relu1(input)
return output
#使用神经网络
xiao = xiaoxiao()

output = xiao(input)
print(output)
#返回结果:
# tensor([[[[1., 0.],
# [0., 3.]]]])

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
# _*_coding:utf-8_*_
#本节我们学习非线性激活 Non-linear Activations (weighted sum, nonlinearity)
#本部分,我们对数据集进行非线性操作
import torch
import torchvision #(dataset dataloader)
#构建数据集
from torch import nn
from torch.nn import Sigmoid
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

dataset = torchvision.datasets.CIFAR10("conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)

#加载数据集
dataloader = DataLoader(dataset,batch_size=64)

#创建神经网络
class xiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiao, self).__init__()
self.sigmoid = Sigmoid()
def forward(self,inpurt):
output = self.sigmoid(inpurt)
return output
#使用神经网络
xiao = xiaoxiao()
#同时要实现可视化处理,创建存放事例的文件夹
writer = SummaryWriter("sigmoid")
step = 0
#使用数据
for data in dataloader:
imgs,targets = data
writer.add_images("unchanged",imgs,global_step=step)
output = xiao(imgs)
writer.add_images("be_sigmoided",output,global_step=step)
step += 1
#close
writer.close()

#运行查看结果

#报错了:TypeError: __init__() takes 1 positional argument but 2 were given
#检查代码的时候,记得看代码上的高亮提示
# self.sigmoid = Sigmoid(input) Sigmoid 里边不需要加参数了

#又报错了:TypeError: 'xiaoxiao' object is not callable
#class xiaoxiao():
#一定要继承Module 类 你现在正在做module中的处理,所以一定要记得要继承
#后面的 super(xiaoxiao, self).__init__() 也是继承,对父类的继承 声明 重点 重点

#经过可视化,我们可以看到,图像进行非线性化处理后,蒙上了一层阴影,色彩的影响因素降低了?
#这也就可以理解,非线性化为什么可以提升给模型的拟合能力了

神经网络-线性层及其他层的介绍

线性层(也称为全连接层、密集层或仿射变换层)是神经网络中的一种基本层,它的作用是对输入数据执行线性变换。线性层包含一个权重矩阵和一个偏置向量。对于给定的输入数据,线性层将权重矩阵与输入数据相乘,然后加上偏置向量,从而实现线性变换。

在数学表示中,线性层可以表示为:

1
y = Wx + b

其中,y 是输出数据,x 是输入数据,W 是权重矩阵,b 是偏置向量。

在 PyTorch 中,线性层由 torch.nn.Linear 类实现。要创建一个线性层,你需要指定输入特征数(即输入数据的大小)和输出特征数(即输出数据的大小)。例如:

1
2
3
import torch.nn as nn

linear_layer = nn.Linear(in_features=3, out_features=2)

这个例子中创建的线性层将输入数据(具有 3 个特征)转换为输出数据(具有 2 个特征)。权重矩阵的大小为 (2, 3),偏置向量的大小为 (2,)。在神经网络中,线性层通常用于将数据从一个维度映射到另一个维度,或者实现多层神经网络的连接。

官方文档

https://pytorch.org/docs/stable/generated/torch.nn.Linear.html#torch.nn.Linear

image

使用flatten将多维转换为线性

在 PyTorch 中,flatten 操作用于将多维张量展平为一维张量。该操作保留张量中的元素,但会移除多维结构,将所有元素放入一个一维张量中。

在 PyTorch 中,你可以使用 torch.flatten() 函数来展平张量。这个函数接受两个可选参数:start_dimend_dim,用于指定从哪个维度开始展平,以及在哪个维度结束展平。

image

示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
import torch

# 创建一个 2x3x4 的张量
tensor = torch.rand(2, 3, 4)
print("Original tensor shape:", tensor.shape)

# 将张量展平为一维
flattened_tensor = torch.flatten(tensor)
print("Flattened tensor shape:", flattened_tensor.shape)

# 从第 1 维开始展平(保留批量维度)
flattened_tensor_start_dim = torch.flatten(tensor, start_dim=1)
print("Flattened tensor with start_dim=1 shape:", flattened_tensor_start_dim.shape)

输出:

1
2
3
Original tensor shape: torch.Size([2, 3, 4])
Flattened tensor shape: torch.Size([24])
Flattened tensor with start_dim=1 shape: torch.Size([2, 12])

在这个例子中,我们首先创建了一个形状为 [2, 3, 4] 的张量。接着,我们将整个张量展平为一个一维张量,其形状为 [24]。最后,我们从第 1 维(索引为 1)开始展平,保留了批量维度(索引为 0),得到形状为 [2, 12] 的张量。

示例

观察下面的图片,1×1×4096 经过全连接层(线性层)后变为了1×1×1000

image

将一个25×25的图像展开成一个长度为25 的向量,再转换成长度为3的一个向量

image

示例:[64, 3, 32, 32] -> [196608] -> [10]

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

#下面我们对数据集来进行一下非线性处理

#创建数据集
dataset = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)

#加载数据集
dataloader = DataLoader(dataset,batch_size=64)

#创建神经网络:
class xiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiao, self).__init__()
self.linear = Linear(196608,10)
def forward(self,input):
output = self.linear(input)
return output

xiao = xiaoxiao()

for data in dataloader:
imgs,targets = data
print(imgs.shape)

output = torch.flatten(imgs)
print(output.shape)

output = xiao(output)
print(output.shape)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
Files already downloaded and verified
torch.Size([64, 3, 32, 32])
torch.Size([196608])
torch.Size([10])
torch.Size([64, 3, 32, 32])
torch.Size([196608])
torch.Size([10])
torch.Size([64, 3, 32, 32])
torch.Size([196608])
torch.Size([10])
torch.Size([64, 3, 32, 32])
torch.Size([196608])
torch.Size([10])
torch.Size([64, 3, 32, 32])
torch.Size([196608])
torch.Size([10])
……

这个代码片段的目的是在 CIFAR-10 数据集上创建一个简单的线性分类器。CIFAR-10 是一个包含 10 个类别的图像分类数据集,每张图像大小为 3x32x32(3 个通道,32x32 像素)。

  1. 导入所需库:导入 PyTorch、torchvision、神经网络模块、线性层、数据加载器和 TensorBoard 的 SummaryWriter。
  2. 创建数据集:使用 torchvision.datasets.CIFAR10 创建一个 CIFAR-10 数据集实例。这将下载数据集并将其转换为 PyTorch 张量。
  3. 加载数据集:使用 DataLoader 加载数据集,设置批次大小为 64。
  4. 创建神经网络:定义一个名为 xiaoxiao 的神经网络类,它继承自 nn.Module在这个类中,定义一个线性层 self.linear,输入大小为 196608(即 3x32x32,因为我们要将图像展平为一个向量),输出大小为 10(CIFAR-10 的类别数量)。forward 函数实现了输入数据的线性变换。
  5. 实例化神经网络:创建一个 xiaoxiao 类的实例 xiao
  6. 循环处理数据:对于数据加载器中的每个批次数据,执行以下操作: a. 获取图像和标签:将批次数据分解为 imgstargets。 b. 展平图像:将图像张量展平,从形状 [batch_size, 3, 32, 32] 变为 [batch_size, 196608]。 c. 应用神经网络:将展平后的图像张量传递给神经网络 xiao,输出形状为 [batch_size, 10]

注意:在这个代码片段中,没有计算损失、执行梯度下降优化或评估模型性能。这只是一个简单的例子,用于说明如何在 CIFAR-10 数据集上创建一个线性分类器。在实际应用中,还需要添加损失函数、优化器、训练和评估循环等。

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
# _*_coding:utf-8_*_
#本节我们来学习线性性层,并对其他层做一个简单的介绍
#Normalization Layers 正则化 也叫标准化层
'''
BatchNorm2d

class
torch.nn.BatchNorm2d(num_features, eps=1e-05, momentum=0.1,
affine=True, track_running_stats=True, device=None, dtype=None)[source]

Applies Batch Normalization over a 4D input
(a mini-batch of 2D inputs with additional 额外的 channel dimension 维度)
as described 描述 in the paper Batch Normalization:
Accelerating 加速 Deep Network Training by Reducing Internal Covariate Shift 减少内部协变量偏移 .


Parameters

num_features – CCC from an expected input of size (N,C,H,W)(N, C, H, W)(N,C,H,W)
输入格式 注意
eps – a value added to the denominator for numerical stability. Default: 1e-5

momentum – the value used for the running_mean and running_var computation.
Can be set to None for cumulative moving average (i.e. simple average). Default: 0.1

affine – a boolean value that when set to True, this module has learnable affine parameters.
Default: True

track_running_stats – a boolean value that when set to True,
this module tracks the running mean and variance,
and when set to False, this module does not track such statistics,
and initializes statistics buffers running_mean and running_var as None.
When these buffers are None, this module always uses batch statistics. in both training and eval modes.
Default: True

Examples:
#
# >>> # With Learnable Parameters 具有可学习的参数
# >>> m = nn.BatchNorm2d(100)
# >>> # Without Learnable Parameters 没有可学习的参数
# >>> m = nn.BatchNorm2d(100, affine=False)

# >>> input = torch.randn(20, 100, 35, 45)
# >>> output = m(input) 这里就是做了一个标准化

'''
'''
Recurrent 经常性的 Layers 多特用于文字识别领域
Transformer 变压器 转换 Layers 多用于nlp
Dropout 主要是为了防止产生过拟合 可以随机的将输入的tensor中的元素变为0 Layers
Sparse 稀疏 Layers 主要应用于自然语言处理中的 稀疏化

'''

'''
我们重点来介绍一下线性层
Linear

class
torch.nn.Linear(in_features, out_features, bias=True, device=None, dtype=None)[source]

Applies a linear transformation to the incoming data: y=xAT+by = xA^T + by=xAT+b
y = w * x + b 类似于感知器
This module supports TensorFloat32.

Parameters

in_features – size of each input sample
输入层中元素的大小
out_features – size of each output sample
输出层中元素的大小
bias – If set to False, the layer will not learn an additive bias. Default: True
是否增加偏置
也就是相当于 将数据从一个长度 转化为 另外的一个长度

'''
import torch
import torchvision
from torch import nn
from torch.nn import Linear
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

#下面我们对数据集来进行一下非线性处理

#创建数据集
dataset = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)

#加载数据集
dataloader = DataLoader(dataset,batch_size=64)

#创建神经网络:
class xiaoxiao(nn.Module): #一定要继承 nn.Module
def __init__(self):
super(xiaoxiao, self).__init__()
self.linear = Linear(196608,10)
def forward(self,input):
output = self.linear(input)
return output

#使用神经网络
xiao = xiaoxiao()
#同时想要实现可视化
# writer = SummaryWriter("./conv")
# step = 0
#使用数据
for data in dataloader:
imgs,targets = data
print(imgs.shape)
# writer.add_images("unchanged",imgs,global_step=step)
# output = torch.reshape(imgs,(1,1,1,-1))
output = torch.flatten(imgs)
print(output.shape)
output = xiao(output)
print(output.shape)
# output = torch.reshape(output,(1,3,1,1))
# writer.add_images("lineared",output,global_step=step)

#关闭
writer.close()
#运行


#出现了一个问题:TypeError: __init__() missing 2 required positional arguments: 'in_features' and 'out_features'
#我们不知道一开始的图像的大小是什么 所以我们要先把 class 注释,来测试一下数据集
#返回结果:
# torch.Size([64, 3, 32, 32])
# 图像的size有四维,我们只想要一个维度,所以要做一个 reshape
# output = torch.reshape(imgs,(1,1,1,-1))
#让它自己计算最后的大小
#返回结果:
# torch.Size([1, 1, 1, 196608])
#所以知道了 linear的in_features (特征)
#从这个特征,我们也可以理解到这个linear的意义,即将特征数量减小 以此来映射到最后需要的分出的类别
#除了用reshape的方式 我们还可以用展平 函数
# ouput = torch.flatten(imgs)
'''
Example:

>>> t = torch.tensor([[[1, 2],
... [3, 4]],
... [[5, 6],
... [7, 8]]])
>>> torch.flatten(t)
tensor([1, 2, 3, 4, 5, 6, 7, 8])
>>> torch.flatten(t, start_dim=1)
tensor([[1, 2, 3, 4],
[5, 6, 7, 8]])

'''

#linear d的输出是我们自己规定的 这里设置为10 对应CIFAR-10 的10个类别
#因此,我们取消注释,填上参数,继续操作

#报错了:
# AssertionError: size of input tensor and input format are different. tensor shape: (10,), input_format: NCHW
#也十分容易理解,可视化要是四维的

#返回结果:
# output = torch.reshape(output, (1, 3, 1, 1))

#仍然报错:
# RuntimeError: shape '[1, 3, 1, 1]' is invalid for input of size 10

#那就不要可视化了 嘿嘿嘿

# 还是有一点报错:
# RuntimeError: mat1 and mat2 shapes cannot be multiplied (1x49152 and 196608x10)
# 但是转换已经做好了

神经网络-搭建小实战和Sequential的使用

在 PyTorch 中,torch.nn.Sequential 是一个容器类,它用于将多个神经网络模块(层)组合成一个更复杂的神经网络。通过将模块按照它们在构造函数中的顺序添加到容器中,你可以轻松地堆叠多个层来构建一个神经网络。当你将输入传递给 Sequential 容器时,它将按照定义的顺序依次将输入传递给每个模块,并将前一个模块的输出作为下一个模块的输入。

下面是一个使用 Sequential 构建的简单多层感知器(MLP)神经网络的示例:

1
2
3
4
5
6
7
8
9
10
11
import torch
from torch import nn

model = nn.Sequential(
nn.Linear(784, 128),
nn.ReLU(),
nn.Linear(128, 64),
nn.ReLU(),
nn.Linear(64, 10),
nn.Softmax(dim=1)
)

在这个例子中,我们定义了一个具有以下结构的 MLP:

  1. 输入层:大小为 784(例如,28x28 的图像展平后的尺寸)。
  2. 隐藏层 1:大小为 128。
  3. ReLU 激活函数。
  4. 隐藏层 2:大小为 64。
  5. ReLU 激活函数。
  6. 输出层:大小为 10(例如,用于分类任务的类别数)。
  7. Softmax 激活函数,用于将输出转换为概率分布。

当你将输入传递给这个模型时,它会按照定义的顺序依次应用每个层。

官方文档

https://pytorch.org/docs/stable/generated/torch.nn.Sequential.html#torch.nn.Sequential

image

image

image

示例

这是我们学习用的CIFAR10数据集的模型结构

PS:图片中少了一个线性层:1024,在64@4×4后面,因为flatter展开后为64×4×4=1024,在进行一个线性层才为64,在一个线性层为10

image-20230319203646125

代码中去实现:

PS:其中卷积层中的padding参数我们要自己去算出来,其中dilation默认是1,我们假设步长stride是1,其他都是已知量,我们可以算出padding

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter


class xiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiao, self).__init__()
self.conv1 = Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2)
self.maxpool1 = MaxPool2d(2)
self.conv2 = Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2)
self.maxpool2 = MaxPool2d(2)
self.conv3 =Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2)
self.maxpool3 = MaxPool2d(2)
self.flatten = Flatten()
self.linear_1 = Linear(in_features=1024,out_features=64)
self.linear_2 = Linear(in_features=64,out_features=10)
def forward(self,x):
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.maxpool3(x)
x = self.flatten(x)
x = self.linear_1(x)
x = self.linear_2(x)
return x

xiao = xiaoxiao()

#测试一下网络能否正常运行并输出正确结果
input = torch.ones((64,3,32,32))
output = xiao(input)
print(output.shape) #输出:torch.Size([64, 10]) 正确

用sequential 的方式来创建这个神经网络:使代码更加简洁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class xiaoxiaoxiao(nn.Module): 
def __init__(self):
super(xiaoxiaoxiao, self).__init__()
self.model = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2, stride=1),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(in_features=1024, out_features=64),
Linear(in_features=64, out_features=10)
)
def forward(self,x):
x = self.model(x)
return x

可视化:add_graph下面对这个网络进行可视化 生成计算图

image

1
2
3
4
5
from torch.utils.tensorboard import SummaryWriter

writer = SummaryWriter("sequential")
writer.add_graph(xiao1,input)
writer.close()

image

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
# _*_coding:utf-8_*_
#本节我们来学习一下 containers 中的 sequential 部分,并且来搭建一个简单的网络
'''
sequential: sequential 顺序的 简单来说就是将神经网络整合到一起 更方便的表示
Example:

# Using Sequential to create a small model. When `model` is run,
# input will first be passed to `Conv2d(1,20,5)`. The output of
# `Conv2d(1,20,5)` will be used as the input to the first
# `ReLU`; the output of the first `ReLU` will become the input
# for `Conv2d(20,64,5)`. Finally, the output of
# `Conv2d(20,64,5)` will be used as input to the second `ReLU`
model = nn.Sequential(
nn.Conv2d(1,20,5),
nn.ReLU(),
nn.Conv2d(20,64,5),
nn.ReLU()
)

# Using Sequential with OrderedDict. This is functionally the
# same as the above code
model = nn.Sequential(OrderedDict([
('conv1', nn.Conv2d(1,20,5)),
('relu1', nn.ReLU()),
('conv2', nn.Conv2d(20,64,5)),
('relu2', nn.ReLU())
]))

'''

#下面我们分别用 sequential 的方式 和 原始的方式 来写一个简单的神经网络

#对于此网络 卷积前后 图像的H W 没有变化 ,也就要是说 padding = 2,stride = 1

#用原始的方式来构建神经网络
import torch
from torch import nn
from torch.nn import Conv2d, MaxPool2d, Flatten, Linear, Sequential
from torch.utils.tensorboard import SummaryWriter


class xiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiao, self).__init__()
self.conv1 = Conv2d(in_channels=3,out_channels=32,kernel_size=5,padding=2,stride=1)
self.maxpool1 = MaxPool2d(2)
self.conv2 = Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2)
self.maxpool2 = MaxPool2d(2)
self.conv3 =Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2)
self.maxpool3 = MaxPool2d(2)
self.flatten = Flatten()
self.linear_1 = Linear(in_features=1024,out_features=64)
self.linear_2 = Linear(in_features=64,out_features=10)
def forward(self,x):
x = self.conv1(x)
x = self.maxpool1(x)
x = self.conv2(x)
x = self.maxpool2(x)
x = self.conv3(x)
x = self.maxpool3(x)
x = self.flatten(x)
x = self.linear_1(x)
x = self.linear_2(x)
return x
#使用此神经网络
xiao = xiaoxiao()
#来输出一下这个网络,看看情况
print(xiao)
#我们来使用pytorch 内置的一些张量设置方式来 测试一下这个神经网络的正确与否
input = torch.ones((64,3,32,32))
output = xiao(input)
print(output.shape)

#报错了:RuntimeError: mat1 and mat2 shapes cannot be multiplied (64x4096 and 1024x64)
#linear中错了
#所以我们在网络的forward之中删掉 linear 运行一下 来找一下linear 的输入
#torch.Size([64, 4096]) 哦,是4096 为什么不是1024呢?
#还是用它计算出来的值吧,4096 也就说 展开后是 64张 4096 的图片

#后来又发现了问题所在,原来在xiaoxiao_forward中 少写了一个池化层

#所以我们加上linear 再运行一下
#返回结果:
# torch.Size([64, 10])
#好了

#用sequential 的方式来创建这个神经网络
class xiaoxiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiaoxiao, self).__init__()
self.model = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2, stride=1),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(in_features=1024, out_features=64),
Linear(in_features=64, out_features=10)
)
def forward(self,x):
x = self.model(x)
return x
xiao1 = xiaoxiaoxiao()
#检验一下网络:
print(xiao1)
input = torch.ones((64,3,32,32))
output = xiao1(input)
print(output.shape)
#对了

#下面对这个网络进行可视化 生成计算图

writer = SummaryWriter("sequential")

writer.add_graph(xiao1,input)

#关闭
writer.close()

#运行查看结果

损失函数和反向传播

损失函数

损失函数(Loss function),也称为目标函数(Objective function)或代价函数(Cost function),是用于衡量机器学习模型预测结果与真实结果之间的差异的函数。在训练过程中,机器学习模型的目标是最小化损失函数,以此来提高模型的预测准确性。

损失函数可以有很多种形式,取决于问题的性质和模型的类型。以下是一些常用的损失函数:

  1. 均方误差(MSE,Mean Squared Error):MSE 是回归问题中常用的损失函数,计算预测值与真实值之间的平方误差的均值。MSE 的值越小,表示模型的预测能力越好。
  2. 交叉熵损失(Cross-entropy loss):交叉熵损失常用于分类问题,特别是多分类问题。它衡量的是模型预测概率分布与真实概率分布之间的差异。交叉熵损失越小,表示模型预测的概率分布越接近真实概率分布。
  3. Hinge损失(Hinge loss):Hinge损失常用于支持向量机(SVM)中的分类问题。它计算的是预测值与真实值之间的距离。当预测正确且置信度较高时,Hinge损失趋于0;当预测错误或置信度较低时,Hinge损失较大。
  4. 平均绝对误差(MAE,Mean Absolute Error):MAE 是回归问题中的另一种损失函数,计算预测值与真实值之间的绝对误差的均值。与均方误差相比,MAE 对异常值不敏感,因此在存在异常值的情况下表现更稳定。
  5. 对数损失(Log loss):对数损失也称为对数似然损失,主要用于二分类问题。它计算的是模型预测概率的对数似然。对数损失越小,表示模型预测的概率分布越接近真实概率分布。

这些损失函数只是众多损失函数中的一部分。在实际应用中,还可以根据问题的具体需求设计自定义损失函数。总之,损失函数是衡量模型性能和指导模型优化的关键。

反向传播

反向传播(Backpropagation)是一种优化神经网络权重和偏置的算法。它是一种监督学习方法,通过计算损失函数关于网络参数(权重和偏置)的梯度,然后使用梯度下降或其他优化算法来更新参数,从而最小化损失函数。

反向传播算法分为以下几个步骤:

  1. 前向传播:将输入数据传递给网络,计算每一层的输出,直到得到最后的预测结果。在这个过程中,每一层都会应用其激活函数和权重矩阵。
  2. 计算损失:使用损失函数计算网络预测结果与真实目标值之间的差异。
  3. 反向传播误差:从输出层开始,通过计算损失函数关于每一层参数的梯度来反向传播误差。这个过程中,链式法则(Chain Rule)在计算复合函数梯度时发挥着关键作用。
  4. 更新权重和偏置:使用计算出的梯度来更新网络中的权重和偏置。这里通常会采用梯度下降或其他优化算法来进行权重更新。

反向传播算法是深度学习和神经网络中最基本的优化算法。通过这个算法,神经网络可以在训练过程中不断地调整参数,使得预测结果更接近真实目标值。

image

损失函数和反向传播的关系密切

损失函数用于衡量模型的预测结果与真实结果之间的差距。反向传播算法是一种用于优化神经网络权重的方法,它通过最小化损失函数来提高模型的预测能力。

损失函数和反向传播的关系可以概括如下:

  1. 前向传播:模型接收输入并计算输出。这个过程中,输入通过隐藏层逐层传递,最后得到输出结果。
  2. 计算损失:使用损失函数比较模型的输出结果与真实结果,得到损失值。损失值越小,表示模型的预测能力越好。
  3. 反向传播:根据损失值计算每个权重参数的梯度。梯度表示损失值相对于权重的变化方向和程度。这个过程从输出层开始,向前逐层传播,直至输入层。计算梯度的方法通常是链式法则和梯度下降算法。
  4. 更新权重:使用梯度更新权重参数,使得下一次迭代时的损失值更小。更新权重的方法通常是梯度下降法(如随机梯度下降、批量梯度下降等)。

通过反复进行前向传播、计算损失、反向传播和更新权重,神经网络能够逐步优化权重参数,提高预测能力。损失函数在这个过程中扮演了衡量模型性能的关键角色,反向传播算法则是优化权重的核心方法。

L1LOSS

官方文档

https://pytorch.org/docs/stable/nn.html#loss-functions

image

示例

image

PS:注意运算时要用浮点

示例:

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch
import torchvision
import winnt
from torch import nn
from torch.nn import L1Loss, MSELoss, Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

inputs = torch.tensor([1,2,3],dtype=torch.float32)
targets = torch.tensor([1,2,5],dtype=torch.float32)

inputs = torch.reshape(inputs,(1,1,1,3))
targets = torch.reshape(targets,(1,1,1,3))

loss = L1Loss()
result = loss(inputs,targets)
print(result) #tensor(0.6667)

PS:有一个参数叫 reduction 减少 ,用它可以来选择不同的 计算方法 这里有 sum 和 mean default= mean

  • reduction ( str , optional ) – 指定要应用于输出的缩减: 'none'| 'mean'| 'sum'. 'none': 不减少, 'mean': 输出的总和将除以输出中元素的数量,'sum': 输出将被求和。注意:size_averagereduce正在被弃用,与此同时,指定这两个参数中的任何一个都将覆盖reduction. 默认:'mean'

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch
import torchvision
import winnt
from torch import nn
from torch.nn import L1Loss, MSELoss, Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

inputs = torch.tensor([1,2,3],dtype=torch.float32)
targets = torch.tensor([1,2,5],dtype=torch.float32)

inputs = torch.reshape(inputs,(1,1,1,3))
targets = torch.reshape(targets,(1,1,1,3))

loss = L1Loss(reduction="sum")
result = loss(inputs,targets)
print(result) #tensor(2.)

MSELOSS

平方差

官方文档

https://pytorch.org/docs/stable/generated/torch.nn.MSELoss.html#torch.nn.MSELoss

image

image

示例

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import torch
import torchvision
import winnt
from torch import nn
from torch.nn import L1Loss, MSELoss, Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

inputs = torch.tensor([1,2,3],dtype=torch.float32)
targets = torch.tensor([1,2,5],dtype=torch.float32)

inputs = torch.reshape(inputs,(1,1,1,3))
targets = torch.reshape(targets,(1,1,1,3))

loss = L1Loss(reduction="sum")
result = loss(inputs,targets)
print(result) #tensor(1.3333)

CROSSENTROPYLOSS

CrossEntropyLoss(交叉熵损失)是一种在分类问题中常用的损失函数。它衡量了真实概率分布(如one-hot编码的类别标签)与模型预测概率分布之间的差异。交叉熵损失的值越小,表示模型的预测能力越好。

在PyTorch中,可以使用torch.nn.CrossEntropyLoss来计算交叉熵损失。需要注意的是,torch.nn.CrossEntropyLoss的输入应该是未经softmax激活函数处理的原始输出(logits)。CrossEntropyLoss会在内部计算softmax激活。

以下是一个简单的示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import torch
import torch.nn as nn

# 假设有4个样本,3个类别
logits = torch.tensor([[1.2, 0.5, 0.8],
[0.3, 1.1, 2.2],
[1.5, 0.7, 1.9],
[0.8, 2.1, 1.6]])

# 真实类别标签
labels = torch.tensor([0, 2, 1, 1])

# 创建交叉熵损失
criterion = nn.CrossEntropyLoss()

# 计算损失
loss = criterion(logits, labels)

print(loss)

在这个例子中,我们有4个样本和3个类别。logits表示模型的原始输出,labels是真实的类别标签。我们使用nn.CrossEntropyLoss来计算损失值。

官方文档

https://pytorch.org/docs/stable/generated/torch.nn.CrossEntropyLoss.html#torch.nn.CrossEntropyLoss

示例1

image

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import torch
import torchvision
import winnt
from torch import nn
from torch.nn import L1Loss, MSELoss, Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

inputs = torch.tensor([1,2,3],dtype=torch.float32)
targets = torch.tensor([1,2,5],dtype=torch.float32)

inputs = torch.reshape(inputs,(1,1,1,3))
targets = torch.reshape(targets,(1,1,1,3))

x = torch.tensor([0.1,0.2,0.3])
y = torch.tensor([1])
x = torch.reshape(x,(1,3))
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x,y)
print(result_cross)

示例2

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
import torch
import torchvision
import winnt
from torch import nn
from torch.nn import L1Loss, MSELoss, Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=1)
#cv
class xiaoxiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiaoxiao, self).__init__()
self.model = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2, stride=1),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(in_features=1024, out_features=64),
Linear(in_features=64, out_features=10)
)
def forward(self,x):
x = self.model(x)
return x

xiao = xiaoxiaoxiao()
for data in dataloader:
img,target = data
outputs = xiao(img)
print(output)
print(target)

1
2
3
4
5
6
7
8
9
10
11
……
tensor([[-0.0925, -0.0038, -0.0186, 0.0261, 0.2027, 0.0677, -0.0544, -0.1516,
-0.0534, -0.0868]], grad_fn=<AddmmBackward0>)
tensor([7])
tensor([[-0.0950, -0.0366, -0.0528, -0.0137, 0.1444, 0.0864, -0.0331, -0.1144,
-0.0483, -0.0642]], grad_fn=<AddmmBackward0>)
tensor([9])
tensor([[-0.0914, -0.0153, -0.0472, 0.0067, 0.1534, 0.0739, -0.0100, -0.1192,
-0.0475, -0.0768]], grad_fn=<AddmmBackward0>)
tensor([4])
……

添加交叉熵测试:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
import torch
import torchvision
import winnt
from torch import nn
from torch.nn import L1Loss, MSELoss, Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=1)
#cv
class xiaoxiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiaoxiao, self).__init__()
self.model = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2, stride=1),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(in_features=1024, out_features=64),
Linear(in_features=64, out_features=10)
)
def forward(self,x):
x = self.model(x)
return x

loss_cross = nn.CrossEntropyLoss()
xiao = xiaoxiaoxiao()
#我们在数据加载器的每个批次上迭代,将图像输入到模型中,并使用交叉熵损失计算损失。计算得到的损失将用于执行反向传播。
for data in dataloader:
imgs,targets = data
outputs = xiao(imgs)
result_loss = loss_cross(outputs,targets)
result_loss.backward()
print(result_loss)
1
2
3
4
5
6
tensor(2.1958, grad_fn=<NllLossBackward0>)
tensor(2.4122, grad_fn=<NllLossBackward0>)
tensor(2.3903, grad_fn=<NllLossBackward0>)
tensor(2.4426, grad_fn=<NllLossBackward0>)
tensor(2.3056, grad_fn=<NllLossBackward0>)
……

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
# _*_coding:utf-8_*_
#本节我们学习损失函数,反向传播
#损失函数:输出 和 taget 目标 之间的差异
#能为我们更新输出提供一定的依据(反向传播)
'''
L1Loss

class
torch.nn.L1Loss(size_average=None, reduce=None, reduction='mean')[source]
都有默认值,所以暂且直接使用吧
Creates a criterion 标准 that measures 计算 the mean absolute error 平均绝对误差 (MAE)
between each element 元素 in the input xxx and target yyy.

Shape:

Input: (∗)(*)(∗), where ∗*∗ means any number of dimensions.

Target: (∗)(*)(∗), same shape as the input.

Output: scalar. If reduction is 'none', then (∗)(*)(∗), same shape as the input.

Examples:
#
# >>> loss = nn.L1Loss()
# >>> input = torch.randn(3, 5, requires_grad=True)
# >>> target = torch.randn(3, 5)
# >>> output = loss(input, target)
# >>> output.backward()

'''

#操作:
import torch
import torchvision
import winnt
from torch import nn
from torch.nn import L1Loss, MSELoss, Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch.utils.data import DataLoader

inputs = torch.tensor([1,2,3],dtype=torch.float32)
targets = torch.tensor([1,2,5],dtype=torch.float32)

inputs = torch.reshape(inputs,(1,1,1,3))
targets = torch.reshape(targets,(1,1,1,3))

loss = L1Loss(reduction="sum") #这其中有一个参数叫 reduction 减少 ,用它可以来选择不同的 计算方法 这里有 sum 和 mean default= mean
result = loss(inputs,targets)
print(result)

#报错了:
# RuntimeError: Can only calculate the mean of floating types. Got Long instead.
#用dtype 继续转化为 float32

#结果:
# tensor(0.6667)
# tensor(2.) 用 sum 的方式来计算
'''
MSELoss

class
torch.nn.MSELoss(size_average=None, reduce=None, reduction='mean')[source]

Creates a criterion that measures the mean 平均 squared 平方 error 差 错误 (squared L2 norm)
between each element in the input xxx and target yyy.


Parameters

size_average (bool, optional) – Deprecated (see reduction).
By default, the losses are averaged over each loss element in the batch. Note that for some losses, there are multiple elements per sample. If the field size_average is set to False, the losses are instead summed for each minibatch. Ignored when reduce is False. Default: True

reduce (bool, optional) – Deprecated (see reduction).
By default, the losses are averaged or summed over observations for each minibatch depending on size_average. When reduce is False, returns a loss per batch element instead and ignores size_average. Default: True

reduction (string, optional) – Specifies the reduction to apply to the output:
'none' | 'mean' | 'sum'. 'none': no reduction will be applied, 'mean':
the sum of the output will be divided by the number of elements in the output, 'sum':
the output will be summed. Note: size_average and reduce are in the process of being deprecated,
and in the meantime, specifying either of those two args will override reduction. Default: 'mean'

都有默认值,我们暂且直接使用好了
Shape:

Input: (∗)(*)(∗), where ∗*∗ means any number of dimensions.

Target: (∗)(*)(∗), same shape as the input.

Examples:

>>> loss = nn.MSELoss()
>>> input = torch.randn(3, 5, requires_grad=True)
>>> target = torch.randn(3, 5)
>>> output = loss(input, target)
>>> output.backward()



'''

lose_mse = MSELoss(reduction="sum")
result_mse = lose_mse(inputs,targets)

print(result_mse)
#结果:
# tensor(1.3333)
# tensor(4.) reduction="sum"

''''
CrossEntropyLoss

class
torch.nn.CrossEntropyLoss(weight=None, size_average=None, ignore_index=- 100,
reduce=None, reduction='mean', label_smoothing=0.0)[source]

This criterion 标准 computes the cross entropy 交叉熵 loss between input and target.

Parameters

weight (Tensor, optional) – a manual rescaling weight given to each class.
If given, has to be a Tensor of size C
权重
size_average (bool, optional) – Deprecated (see reduction).
By default, the losses are averaged over each loss element in the batch. Note that for some losses,
there are multiple elements per sample. If the field size_average is set to False,
the losses are instead summed for each minibatch. Ignored when reduce is False. Default: True

ignore_index (int, optional) – Specifies a target value
that is ignored and does not contribute to the input gradient. When size_average is True,
the loss is averaged over non-ignored targets. Note that ignore_index is only applicable
when the target contains class indices.

reduce (bool, optional) – Deprecated (see reduction).
By default, the losses are averaged or summed over observations for each minibatch depending on size_average.
When reduce is False, returns a loss per batch element instead and ignores size_average. Default: True

reduction (string, optional) –
Specifies the reduction to apply to the output: 'none' | 'mean' | 'sum'. 'none':
no reduction will be applied, 'mean': the weighted mean of the output is taken, 'sum':
the output will be summed. Note: size_average and reduce are in the process of being deprecated,
and in the meantime, specifying either of those two args will override reduction. Default: 'mean'

label_smoothing (float, optional) – A float in [0.0, 1.0].
Specifies the amount of smoothing when computing the loss,
where 0.0 means no smoothing.
The targets become a mixture of the original ground truth
and a uniform distribution as described in Rethinking the Inception Architecture for Computer Vision.
Default: 0.00.00.0.

也是都有默认值,所以我们暂且先就这样使用一下吧
Shape:

Input: (N,C)(N, C)(N,C) where C = number of classes, or (N,C,d1,d2,...,dK)
(N, C, d_1, d_2, ..., d_K)(N,C,d1​,d2​,...,dK​)
with K≥1K \geq 1K≥1 in the case of K-dimensional loss.
注意,这里的输入为 n c n是 batch_size c是 通道数
Target: If containing class indices, shape (N)(N)(N)
where each value is 0≤targets[i]≤C−10 \leq \text{targets}[i] \leq C-10≤targets[i]≤C−1,
or (N,d1,d2,...,dK)(N, d_1, d_2, ..., d_K)(N,d1​,d2​,...,dK​)
with K≥1K \geq 1K≥1 in the case of K-dimensional loss. If containing class probabilities,
same shape as the input.

Output: If reduction is 'none', shape (N)(N)(N) or (N,d1,d2,...,dK)(N, d_1, d_2, ..., d_K)
(N,d1​,d2​,...,dK​) with K≥1K \geq 1K≥1 in the case of K-dimensional loss. Otherwise, scalar.



'''

x = torch.tensor([0.1,0.2,0.3])
y = torch.tensor([1])
x = torch.reshape(x,(1,3))
loss_cross = nn.CrossEntropyLoss()
result_cross = loss_cross(x,y)
print(result_cross)
#结果:
# tensor(1.1019)

#下面,我们对CIFAR-10 这个数据集,利用之前我们做的那个神经网络来做一下 交叉熵损失

dataset = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=1)
#cv
class xiaoxiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiaoxiao, self).__init__()
self.model = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2, stride=1),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(in_features=1024, out_features=64),
Linear(in_features=64, out_features=10)
)
def forward(self,x):
x = self.model(x)
return x

xiao = xiaoxiaoxiao()
for data in dataloader:
imgs,targets = data
outputs = xiao(imgs)
# print(outputs)
# print(targets)
result_loss = loss_cross(outputs,targets)
result_loss.backward()
print(result_loss)
#返回值:
# tensor(1.1019)
# Files already downloaded and verified
# tensor(2.2616, grad_fn=<NllLossBackward>)
# tensor(2.4243, grad_fn=<NllLossBackward>)
# tensor(2.4254, grad_fn=<NllLossBackward>)

#我们说损失可以对反向传播算法中训练参数 有重要的作用,那就是可以求出梯度
#debug : xiao_model_Protacted Attributes 属性_modules 模块_'0'_weight_grad 梯度

优化器

优化器(Optimizer)是深度学习中用于调整模型参数(如神经网络的权重和偏置)的工具,其目标是最小化损失函数,从而提高模型的预测性能。优化器通过计算梯度(损失函数关于模型参数的导数)并根据梯度来更新模型参数。

优化器的作用包括:

  1. 寻找最优解:优化器试图找到能使损失函数最小化的模型参数。这通常涉及在参数空间中寻找局部最优解或全局最优解。
  2. 加速训练:优化器可以使用不同的策略和技巧来加速模型训练过程,如动量(Momentum)和自适应学习率等。
  3. 防止过拟合:一些优化器通过正则化来限制模型的复杂性,从而降低过拟合的风险。例如,L1 和 L2 正则化就是将模型参数的绝对值或平方和添加到损失函数中,惩罚过大的参数值。

常用的优化器有:

  1. 随机梯度下降(Stochastic Gradient Descent,SGD):最简单的优化器,使用单个样本或一小批样本计算梯度并更新模型参数。
  2. 动量优化(Momentum):在 SGD 的基础上增加动量项,以便在梯度方向上加速模型参数的更新。
  3. AdaGrad(Adaptive Gradient):为每个模型参数自适应调整学习率,使得学习率随着梯度的累积逐渐衰减。
  4. RMSProp(Root Mean Square Propagation):改进 AdaGrad 方法,避免学习率过早衰减的问题。
  5. Adam(Adaptive Moment Estimation):结合了动量优化和 RMSProp 的方法,自适应调整学习率,同时保留动量信息。

不同的优化器在不同的任务和模型结构下可能有不同的性能表现。实际应用中,可以尝试多种优化器以找到最适合特定任务的方法。

官方文档

https://pytorch.org/docs/stable/optim.html

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch import optim
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./conv",train=True,transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=64)

class xiaoxiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiaoxiao, self).__init__()
self.model = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2, stride=1),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(in_features=1024, out_features=64),
Linear(in_features=64, out_features=10)
)
def forward(self,x):
x = self.model(x)
return x

loss = nn.CrossEntropyLoss()
xiao = xiaoxiaoxiao()
# optim2 = torch.optim.SGD() 很奇怪 新版本的 torch中没有 optima 了 需要自己手动引入 optim
optim1 = optim.SGD(xiao.parameters(),lr=0.01)
for epoch in range(20):
running_loss = 0.0 #在每一轮开始之前 把loss设置成0
for data in dataloader:
imgs,targets = data
outputs = xiao(imgs)
#首先,先将优化器设置为0
optim1.zero_grad()
result_loss = loss(outputs,targets)
# print("ok")
#再来做一个反向传播
result_loss.backward()
#再来做一个优化的步骤
optim1.step()
# print(result_loss)
running_loss += result_loss #running_loss 也就是计算的在每一轮中 损失函数的总值
print(running_loss)
1
2
3
4
5
6
7
8
9
10
Files already downloaded and verified
tensor(1687.8574, grad_fn=<AddBackward0>)
tensor(1421.9724, grad_fn=<AddBackward0>)
tensor(1287.4739, grad_fn=<AddBackward0>)
tensor(1199.9834, grad_fn=<AddBackward0>)
tensor(1133.7498, grad_fn=<AddBackward0>)
tensor(1077.5430, grad_fn=<AddBackward0>)
tensor(1026.8942, grad_fn=<AddBackward0>)
tensor(980.2069, grad_fn=<AddBackward0>)
……

这段代码是一个完整的神经网络训练流程,加载 CIFAR-10 数据集并使用一个名为 xiaoxiaoxiao 的卷积神经网络进行训练。训练使用了随机梯度下降 (SGD) 优化器和交叉熵损失函数。代码分为以下几个部分:

  1. 导入所需库和数据集。
  2. 定义 xiaoxiaoxiao 神经网络模型。
  3. 设置损失函数(交叉熵损失)。
  4. 创建优化器(随机梯度下降)。
  5. 进行 20 轮的训练(每轮遍历整个数据集)。

在每轮训练中,代码会遍历数据加载器中的所有批次。对于每个批次,我们首先将优化器的梯度清零,然后计算模型的输出和损失,接着执行反向传播以计算梯度。最后,我们使用优化器的 step() 方法更新模型参数。每轮训练结束后,输出该轮的累积损失。

请注意,import torch.optim 已更改为 from torch import optim,这样我们就可以在代码中使用 optim.SGD。其他部分都保持不变。

PS:测试

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
# _*_coding:utf-8_*_
#本节学优化器 torch.optim opitimizer 优化器
#官方文档中的撰写方式变了,优化器有很多种,学一个常用的 随机梯度下降 SGD
'''
SGD

class
torch.optim.SGD(params, lr=<required parameter>, momentum=0, dampening=0, weight_decay=0, nesterov=False)[source]

Implements stochastic 随机 gradient 梯度 descent 下降 (optionally with momentum 动变量 ).
input:γ (lr), θ0 (params), f(θ) (objective), λ (weight decay), μ (momentum), τ (dampening), nesterovfor t=1 to … dogt←∇θft(θt−1)if λ≠0gt←gt+λθt−1if μ≠0if t>1bt←μbt−1+(1−τ)gtelsebt←gtif nesterovgt←gt−1+μbtelsegt←btθt←θt−1−γgtreturn θt\begin{aligned} &\rule{110mm}{0.4pt} \\ &\textbf{input} : \gamma \text{ (lr)}, \: \theta_0 \text{ (params)}, \: f(\theta) \text{ (objective)}, \: \lambda \text{ (weight decay)}, \\ &\hspace{13mm} \:\mu \text{ (momentum)}, \:\tau \text{ (dampening)},\:nesterov\\[-1.ex] &\rule{110mm}{0.4pt} \\ &\textbf{for} \: t=1 \: \textbf{to} \: \ldots \: \textbf{do} \\ &\hspace{5mm}g_t \leftarrow \nabla_{\theta} f_t (\theta_{t-1}) \\ &\hspace{5mm}\textbf{if} \: \lambda \neq 0 \\ &\hspace{10mm} g_t \leftarrow g_t + \lambda \theta_{t-1} \\ &\hspace{5mm}\textbf{if} \: \mu \neq 0 \\ &\hspace{10mm}\textbf{if} \: t > 1 \\ &\hspace{15mm} \textbf{b}_t \leftarrow \mu \textbf{b}_{t-1} + (1-\tau) g_t \\ &\hspace{10mm}\textbf{else} \\ &\hspace{15mm} \textbf{b}_t \leftarrow g_t \\ &\hspace{10mm}\textbf{if} \: nesterov \\ &\hspace{15mm} g_t \leftarrow g_{t-1} + \mu \textbf{b}_t \\ &\hspace{10mm}\textbf{else} \\[-1.ex] &\hspace{15mm} g_t \leftarrow \textbf{b}_t \\ &\hspace{5mm}\theta_t \leftarrow \theta_{t-1} - \gamma g_t \\[-1.ex] &\rule{110mm}{0.4pt} \\[-1.ex] &\bf{return} \: \theta_t \\[-1.ex] &\rule{110mm}{0.4pt} \\[-1.ex] \end{aligned}


Nesterov momentum is based on the formula from On the importance of initialization and momentum in deep learning.

Parameters

params (iterable) – iterable 可迭代的 of parameters to optimize 优化 or dicts defining parameter groups

lr (float) – learning rate
模型.parameters

下面的参数都有默认值,我们暂且先直接使用
momentum (float, optional) – momentum factor (default: 0)
动量
weight_decay (float, optional) – weight decay (L2 penalty) (default: 0)

dampening (float, optional) – dampening for momentum (default: 0)

nesterov (bool, optional) – enables Nesterov momentum (default: False)

Example

# >>> optimizer = torch.optim.SGD(model.parameters(), lr=0.1, momentum=0.9)
# >>> optimizer.zero_grad()
# >>> loss_fn(model(input), target).backward()
# >>> optimizer.step()

'''

#cv
import torchvision
from d2l import torch
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d, Flatten, Linear
from torch import optim
from torch.utils.data import DataLoader

dataset = torchvision.datasets.CIFAR10("./conv",train=True,transform=torchvision.transforms.ToTensor(),download=True)

dataloader = DataLoader(dataset,batch_size=64)

class xiaoxiaoxiao(nn.Module): #啊啊啊啊啊,又忘了继承了
def __init__(self):
super(xiaoxiaoxiao, self).__init__()
self.model = Sequential(
Conv2d(in_channels=3, out_channels=32, kernel_size=5, padding=2, stride=1),
MaxPool2d(kernel_size=2),
Conv2d(in_channels=32, out_channels=32, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Conv2d(in_channels=32, out_channels=64, kernel_size=5, stride=1, padding=2),
MaxPool2d(2),
Flatten(),
Linear(in_features=1024, out_features=64),
Linear(in_features=64, out_features=10)
)
def forward(self,x):
x = self.model(x)
return x

loss = nn.CrossEntropyLoss()
xiao = xiaoxiaoxiao()
# optim2 = torch.optim.SGD() 很奇怪 新版本的 torch中没有 optima 了 需要自己手动引入 optim
optim1 = optim.SGD(xiao.parameters(),lr=0.01)
for epoch in range(20):
running_loss = 0.0 #在每一轮开始之前 把loss设置成0
for data in dataloader:
imgs,targets = data
outputs = xiao(imgs)
#首先,先将优化器设置为0
optim1.zero_grad()
result_loss = loss(outputs,targets)
# print("ok")
#再来做一个反向传播
result_loss.backward()
#再来做一个优化的步骤
optim1.step()
# print(result_loss)
running_loss += result_loss #running_loss 也就是计算的在每一轮中 损失函数的总值
print(running_loss)


#返回结果:
# Files already downloaded and verified
# tensor(2.2979, grad_fn=<NllLossBackward>)
# tensor(2.2872, grad_fn=<NllLossBackward>)
# tensor(2.3024, grad_fn=<NllLossBackward>)
# tensor(2.3057, grad_fn=<NllLossBackward>)

#我们可以看到这个损失函数变化不是很明显 因为每个图像只是优化的了一次,
#而一般来说,我们都要优化好多回的,现在 加一个for 循环用epoch来进行多次训练 并且输出参数

#训练结果:
# Files already downloaded and verified
# tensor(1704.6577, grad_fn=<AddBackward0>)
# tensor(1461.0150, grad_fn=<AddBackward0>)
# tensor(1312.9238, grad_fn=<AddBackward0>)
# tensor(1216.7397, grad_fn=<AddBackward0>)
# 每次的损失函数确实有在下降 说明我们的训练是有效果的

现有网络模型的使用及修改

深度学习领域中有许多现有的网络模型,这些模型针对各种任务进行了优化。以下是一些流行和广泛应用的网络模型:

  1. 卷积神经网络 (CNN):
    • LeNet
    • AlexNet
    • VGGNet (VGG-16, VGG-19)
    • Inception (GoogLeNet, Inception-v3, Inception-v4)
    • ResNet (ResNet-18, ResNet-34, ResNet-50, ResNet-101, ResNet-152)
    • DenseNet
    • MobileNet (MobileNetV1, MobileNetV2, MobileNetV3)
  2. 循环神经网络 (RNN):
    • Vanilla RNN
    • Long Short-Term Memory (LSTM)
    • Gated Recurrent Unit (GRU)
    • Bidirectional RNN (Bi-RNN)
    • Bidirectional LSTM (Bi-LSTM)
    • Attention-based RNN
  3. 变换器 (Transformer):
    • Transformer (基于自注意力机制的模型)
    • BERT (Bidirectional Encoder Representations from Transformers)
    • GPT (Generative Pre-trained Transformer)
    • RoBERTa (Robustly optimized BERT approach)
    • T5 (Text-to-Text Transfer Transformer)
    • DistilBERT (轻量级 BERT 变体)
  4. 自编码器 (AE) 和变分自编码器 (VAE):
    • 基本自编码器
    • 变分自编码器
    • 深度自编码器
    • 卷积自编码器
    • 循环自编码器
  5. 生成对抗网络 (GAN):
    • 基本生成对抗网络
    • Deep Convolutional GAN (DCGAN)
    • Wasserstein GAN (WGAN)
    • Conditional GAN (cGAN)
    • CycleGAN
    • StyleGAN (StyleGAN, StyleGAN2)

这只是深度学习领域众多网络模型的一部分。根据特定任务和数据集,可以选择不同的网络模型来解决问题。在实际应用中,研究人员和工程师通常会根据需要对这些模型进行调整和优化。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
# _*_coding:utf-8_*_
#本节学习对现有模型的加载和修改

#Classification 分类模型
#Semantic Segmentation 语义分割模型

#我们来使用一下 vgg16模型 并且对其进行一个 修改 和 添加
import torchvision.models
from torch import nn

vgg16_false = torchvision.models.vgg16(pretrained=False) #也就是表明这是一个经过训练的原始的模型 只需要加载网络模型即可

vgg16_true = torchvision.models.vgg16(pretrained=True)# 也就是说这是一个已经经过训练的模型 其中包含一些参数 所以需要下载

#分别输出一下看一下区别
print(vgg16_false)
print(vgg16_true)
print("ok")

#我们想要修改一下vgg_false 模型
vgg16_false.classifier[6] = nn.Linear(in_features=4096,out_features=10)
print(vgg16_false)
# (6): Linear(in_features=4096, out_features=10, bias=True) success

#我们想要添加一个线性层,好让其在CIFAR10 上可以良好的分类
vgg16_true.classifier.add_module("7",nn.Linear(in_features=1000,out_features=10))
print(vgg16_true)
# (6): Linear(in_features=4096, out_features=1000, bias=True)
# (7): Linear(in_features=1000, out_features=10, bias=True) success

网络模型的保存与读取

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
# _*_coding:utf-8_*_
#本节我们学习网络模型的保存
import torch
import torchvision
from torch import nn
from torch.nn import Sequential, Conv2d

vgg16 = torchvision.models.vgg16(pretrained=False)
#保存方式一
torch.save(vgg16,"vgg16_method1.pth") #object 要保存的文件的名字 一般将文件设置成 .pth 结尾的文件

#保存方式二(官方推荐)
torch.save(vgg16.state_dict(),"vgg16_method2.pth") #dict 字典 是python中的一个文件类型

#可以用ls -all 来查看一下大下 在文件夹上打开终端

#保存方式用来保存自己的模型时候的陷阱:
class xiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiao, self).__init__()
self.conv1 = Conv2d(in_channels=3,out_channels=64,kernel_size=5)
def forward(self,input):
output = self.conv1(input)
return output
xiao = xiaoxiao()

torch.save(xiao,"xiaoxiao.pth")
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
# _*_coding:utf-8_*_
#本节我们来学习一下网络模型的读取
import torch
#加载方式一 对应保存方式一
import torchvision
from torch import nn
from torch.nn import Conv2d

model = torch.load("vgg16_method1.pth") #第一个参数是引用文件的地址

print(model) #success

#加载方式二 对应保存方式二
# vgg16 = torchvision.models.vgg16(pretrained=False)
# model1 = torch.load("vgg16_method2.pth")
# print(model1)
# OrderedDict([('features.0.weight', tensor([[[[-3.3734e-02, 4.3049e-02, 4.7108e-02],
# [ 7.6039e-02, -7.6000e-02, 3.5358e-02],
# [ 9.0538e-02, 2.1419e-02, -8.4892e-02]],

#我们发现输出结果是字典的形式 里边都是一些参数
#我们怎样使用自己的参数呢?
vgg16 = torchvision.models.vgg16(pretrained=False)
vgg16.load_state_dict(torch.load("vgg16_method2.pth")) #一定要记得加载数据 直接从vgg16 开始写 就不用开局model了
print(vgg16) #直接一步到位的引入并且打开 简洁 more pythonic
#success


#看起来官方推荐的保存方式很麻烦,其实不然,保存方式一会有陷阱
#陷阱:
#已经创建好了自己的网络,想要来加载一下
# model = torch.load("xiaoxiao.pth")
# print(model)
#报错:AttributeError: Can't get attribute 'xiaoxiao' on <module '__main__' from 'D:/pytorch_learning_tudui/p26.py'>
#要怎么来使用呢? 先把上面那两行注释掉
class xiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiao, self).__init__()
self.conv1 = Conv2d(in_channels=3,out_channels=64,kernel_size=5)
def forward(self,input):
output = self.conv1(input)
return output
# xiao = xiaoxiao()
model = torch.load("xiaoxiao.pth")
print(model)

#然后导入需要导入的
# xiaoxiao(
# (conv1): Conv2d(3, 64, kernel_size=(5, 5), stride=(1, 1))
# )
#这样就可以了

#但是这样就更麻烦了 也就是 model = torch.load("xiaoxiao.pth") 替换了 # xiao = xiaoxiao()
#大可不必
#但是在具体的项目中,我们一般使用 from p25 import *
#也就是将使用的神经网络专门写到一个程序里 比如这里是p25
#然后直接将这个程序全部引入到p26 这个专门进行数据处理的程序之中

完整的模型训练套路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
# _*_coding:utf-8_*_
#完整的模型训练套路
#构建神经网路
import torch
from torch import nn
from torch.nn import Sequential, Conv2d, MaxPool2d


class xiaoxiao(nn.Module):
def __init__(self):
super(xiaoxiao, self).__init__()
self.model = Sequential(
nn.Conv2d(in_channels=3,out_channels=32,kernel_size=5,stride=1,padding=2),
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=32,kernel_size=5,stride=1,padding=2),
nn.MaxPool2d(kernel_size=2),
nn.Conv2d(in_channels=32,out_channels=64,kernel_size=5,stride=1,padding=2),
nn.MaxPool2d(kernel_size=2),
nn.Flatten(),
nn.Linear(in_features=64*4*4,out_features=64),
nn.Linear(in_features=64,out_features=10)
)
def forward(self,x):
x = self.model(x)
return x

if __name__ == '__main__': #主干
#测试一下模型是否正确
#注意,要先引入模型
xiao = xiaoxiao()
input = torch.ones((64,3,32,32))
output = xiao(input)
print(output.shape)
#报错了:
# TypeError: __init__() takes 1 positional argument but 2 were given
#没有引入模型
#torch.Size([64, 10]) success

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
# _*_coding:utf-8_*_
#完整的训练套路
#处理数据部分

#引入神经网络模型 (注意,此程序和神经网络模型的程序要在同一个文件夹下)
import torch
import torchvision
from torch.utils.data import DataLoader

from p27_1 import * # * 代表全部引入

#准备数据集
train_data = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10("./conv",train=True,transform=torchvision.transforms.ToTensor(),download=True)

#加载数据集
train_load = DataLoader(train_data,batch_size=64)
test_load = DataLoader(test_data,batch_size=64)

#看一下数据集的长度 length
train_data_len = len(train_data)
test_data_len = len(test_data)
#常见的输出的形式
print("训练数据集的长度:{}".format(train_data_len))
print("验证数据集的长度:{}".format(test_data_len))
# 训练数据集的长度:10000
# 验证数据集的长度:50000

#创建网路模型
xiao = xiaoxiao()

#损失函数
loss_fun = nn.CrossEntropyLoss()

#优化器
learning_rate = 1e-2 #1*10的-2次方
optimizer = torch.optim.SGD(params=xiao.parameters(),lr=learning_rate)

#设置网络模型的一些参数
#记录训练的次数
total_train_step = 0
#记录验证的次数
total_test_step = 0
#训练的轮数
epoch = 10
for i in range(epoch):
print("-----第{}轮训练开始-----".format(i+1)) #为了符合阅读习惯 这里要加一

#训练步骤
for data in train_load:
imgs,targets = data
outputs = xiao(imgs)
loss = loss_fun(outputs,targets)
#优化步骤
optimizer.zero_grad()
loss.backward()
optimizer.step()

total_train_step += 1
print("训练次数:{} loss;{}".format(total_train_step,loss.item())) #.item() 可以将tensor 类型 转化为 单纯的数 int/float

#每训练完一轮,要测试一下,求一下 loss ,来看训练的效果
total_test_loss = 0
with torch.no_grad(): #这只是测试,所以要确保不进行梯度的改动
for data in test_load: #从测试集取数据
imgs,targets = data
outputs = xiao(imgs)
loss = loss_fun(outputs,targets)
total_test_loss += loss
print("整体测试集上的Loss:{}".format(total_test_loss))


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
# _*_coding:utf-8_*_
#优化训练套路

#完整的训练套路
#处理数据部分

#引入神经网络模型 (注意,此程序和神经网络模型的程序要在同一个文件夹下)

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from p27_1 import * # * 代表全部引入

#准备数据集
train_data = torchvision.datasets.CIFAR10("./conv",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)

#加载数据集
train_load = DataLoader(train_data,batch_size=64)
test_load = DataLoader(test_data,batch_size=64)

#看一下数据集的长度 length
train_data_len = len(train_data)
test_data_len = len(test_data)
#常见的输出的形式
print("训练数据集的长度:{}".format(train_data_len))
print("验证数据集的长度:{}".format(test_data_len))
# 训练数据集的长度:10000
# 验证数据集的长度:50000

#创建网路模型
xiao = xiaoxiao()

#损失函数
loss_fun = nn.CrossEntropyLoss()

#优化器
learning_rate = 1e-2 #1*10的-2次方
optimizer = torch.optim.SGD(params=xiao.parameters(),lr=learning_rate)

#做可视化处理
writer = SummaryWriter("xunlian")

#设置网络模型的一些参数
#记录训练的次数
total_train_step = 0
#记录验证的次数
total_test_step = 0
#训练的轮数
epoch = 10
for i in range(epoch):
print("-----第{}轮训练开始-----".format(i+1)) #为了符合阅读习惯 这里要加一

#训练步骤
xiao.train()
for data in train_load:
imgs,targets = data
outputs = xiao(imgs)
loss = loss_fun(outputs,targets)
#优化步骤
optimizer.zero_grad()
loss.backward()
optimizer.step()

total_train_step += 1
if total_train_step % 100 == 0: #优化一
print("训练次数:{} loss;{}".format(total_train_step,loss.item())) #.item() 可以将tensor 类型 转化为 单纯的数 int/float
writer.add_scalar("train_loss",loss.item(),global_step=total_train_step)
#每训练完一轮,要测试一下,求一下 loss ,来看训练的效果
xiao.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): #这只是测试,所以要确保不进行梯度的改动
for data in test_load: #从测试集取数据
imgs,targets = data
outputs = xiao(imgs)
loss = loss_fun(outputs,targets)
total_test_loss += loss.item()
accuracy = (outputs.argmax(1) == targets).sum() #等于就表示为1 不能于就表示为0
total_accuracy += accuracy
print("整体测试集上的Loss:{}".format(total_test_loss))
print("整体测试的正确率:{}".format(total_accuracy/test_data_len)) #正确率 等于 正确的除以总体的个数
writer.add_scalar("total_test_loss",scalar_value=total_test_loss,global_step=total_test_step)
writer.add_scalar("total_accuracy",scalar_value=total_accuracy,global_step=total_test_step)
total_test_step += 1 #测试一次要加一 每轮测试一次 测试的次数和训练的轮数是相同的

# torch.save(xiao,"xiao_{}.pth".format(i))
#改为官方推荐的保存模型的方式
torch.save(xiao.state_dict(),"xiao_{}".format(i))

print("模型已保存")
#优化:
#1 : 我们发现每一次的训练次数和loss都计算出来了 但是太多了 以至于把total_test_loss 掩盖掉了
#所以我们就想着优化一下步骤,减少训练次数的缺失
#if total_train_step % 100 == 0:

#2 : 发现我把训练数据集和验证数据集搞反了
#train = True 为 训练数据集
#train = False 为 测试数据集
#3 : 用tensorboard做可视化处理
#输出每训练100次的loss
#输出每轮训练的测试数据的total_loss
#4: 保存每一轮我们训练的模型
#5: 我们发现模型可视化中 total_test_loss 并没有发生变化 原因是 写入图像的时候的错误了
# loss -> total_test_loss
#6: 继续优化,我们total_test_loss 还是不能特别直观的显示出我们模型训练的好坏
#所以我们来计算一下正确率
#.argmax(1) 表示横向比较 从0 到1 哪个大 就把哪个设置为1
#.argmax(0) 表示纵向比较 从0 到1 哪个大 就把哪个设置为1
#例子 p28_1
#7: 加上两个起始标志函数
#xiao.train()
#xiao.eval()
#只是对特定的网络模型起作用 Dropout BatchNorm etc.
#8: 改为官方推荐的保存模型的方式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# _*_coding:utf-8_*_
#对于求正确率的测试和例子
import torch

outputs = torch.tensor([[0.1,0.2],
[0.3,0.4]]) #注意 这是二维的 所以要有两个] 一开始写的时候就应该想好
print(outputs.argmax(1))
preds = outputs.argmax(1)
targets = torch.tensor([0,1])
print((preds == targets).sum())
#这就是正确的个数的加和

# tensor([1, 1])
# tensor(1)
# success 符合预期

利用GPU训练

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
# _*_coding:utf-8_*_
#让模型在GPU 上跑
#方式一 .cuda
#只需要在 网络模型 数据(输入,标注) 损失函数 这三个方面加上.cuda 就可以了
#最开始引入数据集的时候 可能不支持
#后面有train和test两方面的数据 注意都要改

#完整的训练套路
#处理数据部分

#引入神经网络模型 (注意,此程序和神经网络模型的程序要在同一个文件夹下)

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from p27_1 import * # * 代表全部引入

#准备数据集
train_data = torchvision.datasets.CIFAR10("./conv",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)

#加载数据集
train_load = DataLoader(train_data,batch_size=64)
test_load = DataLoader(test_data,batch_size=64)

#看一下数据集的长度 length
train_data_len = len(train_data)
test_data_len = len(test_data)
#常见的输出的形式
print("训练数据集的长度:{}".format(train_data_len))
print("验证数据集的长度:{}".format(test_data_len))
# 训练数据集的长度:10000
# 验证数据集的长度:50000

#创建网路模型
xiao = xiaoxiao()
# xiao = xiaoxiao.cuda() #GPU 可以不写等于 也可以写等于
if torch.cuda.is_available():
xiao.cuda()

#损失函数
loss_fun = nn.CrossEntropyLoss()
# loss_fun = nn.CrossEntropyLoss.cuda() #GPU 可以不写等于 也可以写等于
if torch.cuda.is_available():
loss_fun.cuda()

#优化器
learning_rate = 1e-2 #1*10的-2次方
optimizer = torch.optim.SGD(params=xiao.parameters(),lr=learning_rate)

#做可视化处理
writer = SummaryWriter("xunlian")

#设置网络模型的一些参数
#记录训练的次数
total_train_step = 0
#记录验证的次数
total_test_step = 0
#训练的轮数
epoch = 10
for i in range(epoch):
print("-----第{}轮训练开始-----".format(i+1)) #为了符合阅读习惯 这里要加一

#训练步骤
xiao.train()
for data in train_load:
imgs,targets = data
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
outputs = xiao(imgs)
loss = loss_fun(outputs,targets)
#优化步骤
optimizer.zero_grad()
loss.backward()
optimizer.step()

total_train_step += 1
if total_train_step % 100 == 0: #优化一
print("训练次数:{} loss;{}".format(total_train_step,loss.item())) #.item() 可以将tensor 类型 转化为 单纯的数 int/float
writer.add_scalar("train_loss",loss.item(),global_step=total_train_step)
#每训练完一轮,要测试一下,求一下 loss ,来看训练的效果
xiao.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): #这只是测试,所以要确保不进行梯度的改动
for data in test_load: #从测试集取数据
imgs,targets = data
if torch.cuda.is_available():
imgs = imgs.cuda()
targets = targets.cuda()
outputs = xiao(imgs)
loss = loss_fun(outputs,targets)
total_test_loss += loss.item()
accuracy = (outputs.argmax(1) == targets).sum() #等于就表示为1 不能于就表示为0
total_accuracy += accuracy
print("整体测试集上的Loss:{}".format(total_test_loss))
print("整体测试的正确率:{}".format(total_accuracy/test_data_len)) #正确率 等于 正确的除以总体的个数
writer.add_scalar("total_test_loss",scalar_value=total_test_loss,global_step=total_test_step)
writer.add_scalar("total_accuracy",scalar_value=total_accuracy,global_step=total_test_step)
total_test_step += 1 #测试一次要加一 每轮测试一次 测试的次数和训练的轮数是相同的

torch.save(xiao.state_dict(),"xiao_{}".format(i))

print("模型已保存")

image

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
# _*_coding:utf-8_*_
#将模型放在GPU 上跑
#第二中方式 to(device)
#进行一个计时操作
#device = torch.device("cuda:0") #指定第二个显卡
#device = torch.device("cuda:1") #指定使用第二个显卡


#完整的训练套路
#处理数据部分

#引入神经网络模型 (注意,此程序和神经网络模型的程序要在同一个文件夹下)

import torchvision
from torch.utils.data import DataLoader
from torch.utils.tensorboard import SummaryWriter

from p27_1 import * # * 代表全部引入

#引入时间计量
import time

#定义要训练的设备:
device = torch.device("cuda" if torch.cuda.is_available() else "cpu") #利用语法糖的形式写

#准备数据集
train_data = torchvision.datasets.CIFAR10("./conv",train=True,transform=torchvision.transforms.ToTensor(),download=True)
test_data = torchvision.datasets.CIFAR10("./conv",train=False,transform=torchvision.transforms.ToTensor(),download=True)

#加载数据集
train_load = DataLoader(train_data,batch_size=64)
test_load = DataLoader(test_data,batch_size=64)

#看一下数据集的长度 length
train_data_len = len(train_data)
test_data_len = len(test_data)
#常见的输出的形式
print("训练数据集的长度:{}".format(train_data_len))
print("验证数据集的长度:{}".format(test_data_len))
# 训练数据集的长度:10000
# 验证数据集的长度:50000

#创建网路模型
xiao = xiaoxiao()

# # xiao = xiaoxiao.cuda() #GPU 可以不写等于 也可以写等于
# if torch.cuda.is_available():
# xiao.cuda()

xiao.to(device)

#损失函数
loss_fun = nn.CrossEntropyLoss()
# loss_fun = nn.CrossEntropyLoss.cuda() #GPU 可以不写等于 也可以写等于
# if torch.cuda.is_available():
# loss_fun.cuda()
loss_fun.to(device)

#优化器
learning_rate = 1e-2 #1*10的-2次方
optimizer = torch.optim.SGD(params=xiao.parameters(),lr=learning_rate)

#做可视化处理
writer = SummaryWriter("xunlian1")

#设置网络模型的一些参数
#记录训练的次数
total_train_step = 0
#记录验证的次数
total_test_step = 0
#训练的轮数
epoch = 30
#设置开始的时间:
start_time = time.time()
for i in range(epoch):
print("-----第{}轮训练开始-----".format(i+1)) #为了符合阅读习惯 这里要加一

#训练步骤
xiao.train()
for data in train_load:
imgs,targets = data
# if torch.cuda.is_available():
# imgs = imgs.cuda()
# targets = targets.cuda()
imgs = imgs.to(device)
targets = targets.to(device)
outputs = xiao(imgs)
loss = loss_fun(outputs,targets)
#优化步骤
optimizer.zero_grad()
loss.backward()
optimizer.step()

total_train_step += 1
if total_train_step % 100 == 0: #优化一
end_time = time.time()
print("训练100次所要花费的时间:{}".format(end_time-start_time))
print("训练次数:{} loss;{}".format(total_train_step,loss.item())) #.item() 可以将tensor 类型 转化为 单纯的数 int/float
writer.add_scalar("train_loss",loss.item(),global_step=total_train_step)
#每训练完一轮,要测试一下,求一下 loss ,来看训练的效果
xiao.eval()
total_test_loss = 0
total_accuracy = 0
with torch.no_grad(): #这只是测试,所以要确保不进行梯度的改动
for data in test_load: #从测试集取数据
imgs,targets = data
# if torch.cuda.is_available():
# imgs = imgs.cuda()
# targets = targets.cuda()
imgs = imgs.to(device)
targets = targets.to(device)
outputs = xiao(imgs)
loss = loss_fun(outputs,targets)
total_test_loss += loss.item()
accuracy = (outputs.argmax(1) == targets).sum() #等于就表示为1 不能于就表示为0
total_accuracy += accuracy
print("整体测试集上的Loss:{}".format(total_test_loss))
print("整体测试的正确率:{}".format(total_accuracy/test_data_len)) #正确率 等于 正确的除以总体的个数
writer.add_scalar("total_test_loss",scalar_value=total_test_loss,global_step=total_test_step)
writer.add_scalar("total_accuracy",scalar_value=total_accuracy,global_step=total_test_step)
total_test_step += 1 #测试一次要加一 每轮测试一次 测试的次数和训练的轮数是相同的

torch.save(xiao.state_dict(),"xiao_{}".format(i))

print("模型已保存")

#经过训练,我们发现,在训练到第21轮时,模型能力最好
# -----第30轮训练开始-----
# 训练100次所要花费的时间:138.55688571929932
# 训练次数:22700 loss;0.7172563076019287
# 训练100次所要花费的时间:139.06119847297668
# 训练次数:22800 loss;0.6489078402519226
# 训练100次所要花费的时间:139.59840059280396
# 训练次数:22900 loss;0.7892975807189941
# 训练100次所要花费的时间:140.14656686782837
# 训练次数:23000 loss;0.4698319435119629
# 训练100次所要花费的时间:140.69772219657898
# 训练次数:23100 loss;0.4409283697605133
# 训练100次所要花费的时间:141.20226645469666
# 训练次数:23200 loss;0.7165119647979736
# 训练100次所要花费的时间:141.70757579803467
# 训练次数:23300 loss;0.8021339774131775
# 训练100次所要花费的时间:142.21288228034973
# 训练次数:23400 loss;0.5524154901504517
# 整体测试集上的Loss:173.79939830303192
# 整体测试的正确率:0.64410001039505
# 模型已保存

# -----第21轮训练开始-----
# 训练100次所要花费的时间:95.92178964614868
# 训练次数:15700 loss;0.7853329181671143
# 训练100次所要花费的时间:96.4214882850647
# 训练次数:15800 loss;0.8872748613357544
# 训练100次所要花费的时间:96.91782259941101
# 训练次数:15900 loss;0.9362057447433472
# 训练100次所要花费的时间:97.41416621208191
# 训练次数:16000 loss;0.8199751377105713
# 训练100次所要花费的时间:97.90850830078125
# 训练次数:16100 loss;0.7234385013580322
# 训练100次所要花费的时间:98.4041440486908
# 训练次数:16200 loss;0.9981189370155334
# 训练100次所要花费的时间:98.8944993019104
# 训练次数:16300 loss;0.7245725393295288
# 训练100次所要花费的时间:99.38486170768738
# 训练次数:16400 loss;0.745050311088562
# 整体测试集上的Loss:158.07320135831833
# 整体测试的正确率:0.6566999554634094
# 模型已保存

完整的模型验证套路

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
# _*_coding:utf-8_*_
#完整的模型验证套路

#目的是随机找一张测试集之外的图片,来进行测试

#首先要准备验证需要的图片
import torch
import torchvision
from PIL import Image

image_path = "./imgs/img_1.png" #注意写相对路径的方法 穿越几个层级 就写几个. 层级之间有明显的交错
#实在嫌麻烦可以直接复制绝对路径
image = Image.open(image_path).convert("RGB") #from PIL.Image import Image 这样竟然找不到 open 函数
#进行了一个十分重要的 PIL 中 4 channel 转 3 channel
print(image)

#转化图像的类型,还要改变图像的size H W
transform = torchvision.transforms.Compose([torchvision.transforms.Resize((32,32)),
torchvision.transforms.ToTensor()])
img = transform(image)
img = torch.reshape(img,(-1,3,32,32))

#导入模型
from p27_1 import *
xiao = xiaoxiao()
xiao.load_state_dict(torch.load("xiao_20_best_xiao_for_CIFAR10,pth"))
# print(xiao)
#卧槽 气死我了 使用字典的方式 引入新的参数 一定要先初始化模型

ouput = xiao(img)
print(ouput)
print(ouput.argmax(1))
#报错:
#TypeError: conv2d(): argument 'input' (position 1) must be Tensor, not PngImageFile
#哦哦哦 要把图像的类型给转化了

#有报错了:
#RuntimeError: Expected 4-dimensional input for 4-dimensional weight [32, 3, 5, 5],
#but got 3-dimensional input of size [4, 32, 32] instead
#所以reshape一下就好了

#又报错了:
#RuntimeError: shape '[1, 3, 32, 32]' is invalid for input of size 4096
#那就把batch_size 设置为-1 让它自己去算好了填上
#还是不行:RuntimeError: shape '[-1, 3, 32, 32]' is invalid for input of size 4096
#那我们想办法把图片转换成 RGB
#使用 .conver("RGB") 对于PIL 来说

#结果:
# tensor([[-3.2025, 1.3308, -0.3833, 2.4118, -4.2121, 4.4859, -2.5838, -0.6514,
# -1.8818, 2.5838]], grad_fn=<AddmmBackward>)

#优化:不好看哪个是哪个
#直接输出最大的那一个 output.argmax(1) 横向输出最大的那一个
#tensor([5]) success

#如何找模型的对应 去dataset 里的 class
#['airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck']
#哦 预测对了
#再来一个飞机预测一下
# tensor([0])
#哦 也对了!!!

#明天来写一个更智能的应用来方便化分类 顺便来 复习一下之前学的python
#明天还可以考虑用vgg 等诸多 牛逼的已有网络进行训练一波 提升分类器的精度 顺便复习复习 如何应用模型 修改模型之类的


PS:后记

本地配置远程服务器

pycharm通过SSH连接GPU服务器

python虚拟环境如何激活

1、虚拟环境创建后,需要激活才能在当前命令行使用,可以理解为替换当前命令行环境中的PATH变量值。

2、虚拟环境由virtualenv和模块venv创建,激活方式相同,即运行激活脚本。

(1)在Windows系统中,激活脚本路径为\Scripts\activate.bat,如果是powershell命令行,脚本将被Activate.ps1所取代,注意将被自己的虚拟环境目录所取代。

(2)在Linux系统中,激活脚本路径为/bin/activate,默认情况下脚本没有执行权限,或者将脚本设置为可执行,或者使用source命令执行。

1
$ source myvenv/bin/activate

3、激活后,可以在命令行中看到虚拟环境标记。

打印 PATH,命令如下:

Linux 下:

1
echo $PATH

Windows 下

1
echo %PATH%