Pytorch

Package

torch

torch.nn

torch.autograd

torch.nn.functional

torch.optim

torch.utils

torchvision

第一部分查看验证和相关信息

#查看pytorch是否安装成功
import torch
print(torch.__version__)
tor.cuda.is_available()
torch.version.cuda
#查看GPU号
t=torch.tensor([1,2,3])
t
t=t.cuda()
t
输出:tensor([1,2,3]),device="cuda:GPU的符号"

第二部分张量

#index和内容
a = [1,2,3,4]
a[2]
#输出 3
a=[
[1,2,3],
[4,5,6],
[7,8,9]
]
dd[0][2]
#输出3

不适用attay或者matrix类似的定义,用n-tensor/n-array来代表多维数据。但是张量并不代表有多少个维度,如上面dd代表的是9个维度(数学意义上)。一个张量元素代表的是一个维度

张量三个基本元素:秩Rank 轴Axes 形状Shape

秩 考虑的是秩的个数,就是轴的个数。表示Array的维度,需要多少个索引来访问张良数据结构中包含的特定数据元素,比如上面a就是1,dd就是2

rank-2tensor,也是2个秩axes,也就是2维数组

轴 某个方向上索引的个数,轴考虑的是长度。轴的数量就是秩

。每个轴上面的长度,就是这个轴/一个秩下的索引个数。比如一个轴len是3,那么索引就是0 1 2,他就是3dimention(数学)。每个秩包括len(秩)维数字

dd.shape
#输出 torch.Size([3,3])
表示每个轴上有3个索引(轴的长度),一共有两个轴,即秩为2

Reshape过程


import torch
t=[
    [1,2,3],
    [4,5,6],
    [7,8,9]
]
tt = torch.tensor(t)
tt
type(tt)
tt.shape
tt.reshape(1,9)
tt.reshape(1,9).shape
#结果 从tt开始
tensor([[1, 2, 3],
        [4, 5, 6],
        [7, 8, 9]])
torch.Tensor
torch.Size([3, 3])
tensor([[1, 2, 3, 4, 5, 6, 7, 8, 9]])
torch.Size([1, 9])
#注意,torch.size里面数字的乘积前后应该一致

Reshaping改变形状但是不改变底层数组

CNN和张量

通常CNN长度为

len([? , ? , ? , ? ])=4
     A0  A1  A2  A3 四个轴,每个?表示轴长度
     B   C   H   W  图像的宽和长,C表示图像的channal,比如rgb是c=3。B表示batchsize多少个样本
比如[3,1,28,28] 3个图,1个channal,28*28的大小
#假设我们有3个filter,高和宽不做变化
土建[1,1,28,28]经过filter之后变成[1,3,28,28],即3个featuremap

查看设备以及张量的计算

#例子
import torch
import numpy as np
t=[
    [1,2,3]
    ]
tt = torch.Tensor(t)
type(tt)
print(tt.dtype)
print(tt.device)
print(tt.layout)
device=torch.device("cuda:0")
device
tt.device
ttt = tt.cuda()
#结果
torch.Tensor
torch.float32
cpu
torch.strided
device(type='cuda', index=0)
device(type='cpu')
device(type='cuda',index=0)
#注意,这里面ttt+tt是不能操作的,因为在不同的设备之内

张量的创建方法

import numpy as np
import torch
data = np.array([1,2,3])
type(data)
输出 numpy.ndarray
torch.Tensor(data)
#输出 tensor([1., 2., 3.])浮点数     torch.float32                                Constructor function
torch.tensor(data)
#输出 tensor([1, 2, 3], dtype=torch.int32)和np中的数据类型是一样的       torch.int32 factury function
torch.as_tensor(data)
#输出 tensor([1, 2, 3], dtype=torch.int32)和np中的数据类型是一样的    torch.int32     factury function
torch.from_numpy(data)
输出
#tensor([1, 2, 3], dtype=torch.int32)和np中的数据类型是一样的        torch.int32    factury function
get_default_dtype
#输出是 torch.float32

倾向于使用工厂函数,构造函数是默认转换为缺省值(默认值)。工厂函数根据输入函数推断出来,不使用缺省值。

扩展
torch.tensor(data,dtype = torch.float64)
tensor([1, 2, 3], dtype=torch.int64)

四个函数的区别之一:前两个将np映射到tensor然后产生一个copy改变np不改变tensor。但是后两个保留np到tensor的映射,改变np,他们的tensor数值也改变了

data = np.array([1,2,3])
data
#输出array([1,2,3])
t1=torch.Tensor(data)
t2=torch.tensor(data)
t3=torch.as_tensor(data)
t4=torch.from_numpy(data)
data[0]=0
data[1]=0
data[2]=0
print(t1)
print(t2)
#输出 tensor([1., 2., 3.])
#tensor([1, 2, 3], dtype=torch.int32)
print(t4)
print(t5)
#输出 tensor([0, 0, 0], dtype=torch.int32)
tensor([0, 0, 0], dtype=torch.int32)

专注于正确,而不专注于效率:最好的方法使用torch.tensor()和torch.as_tensor(),因为前者复制一个副本,后者可以使用任何的python数组。而另外的Tensor使用的缺省值,而from只能应用于numpy数组。

张 量的一些常用创建函数

import torch
torch.eye(2)
#建立一个对角线是1的2*2张量 浮点数
#tensor([[1., 0.],
#        [0., 1.]])

torch.zeros(2,2)
#建立一个全部都是0的2*2张量 浮点数
#tensor([[0., 0.],
#        [0., 0.]])

torch.ones(2,2)
#建立全部都是1的2*3张量 浮点数
#tensor([[1., 1.],
#        [1., 1.]])

torch.rand(2,2)
#随机建立2*2的张量 浮点数
#tensor([[0.6214, 0.2073],
#        [0.1053, 0.5916]])

Tensor的四大类操作

  • Reshaping operations 重塑 塑造和直观的理解张量
  • Element-wise operations 元素的操作还原和访问
  • Reduction operations 裁剪
  • Access operations 访问
t = torch.tensor([
    [1,1,1,1],
    [2,2,2,2],
    [3,3,3,3]
],dtype=torch.float32)
#注意他的秩为2,第一个轴元素3,第二个轴元素4.第一个轴的元素是四个数字组成的Array,第二个轴的元素是Number(可以理解成数组的编号)
#获得他们的形状
t.size()
t.shape
#输出相同都是torch.Size([3, 4])
len(t.shape)
#输出2,通过len快速获得秩的数目
torch.tensor(t.shape).prod()
#输出12,将shape转化为张量,然后查看张量的元素,即张量的标量分量
t.numel()
#输出12 直接产生元素数目
##只要reshape之后,他们元素不变就行
#####t.reshape(1,12)  或2*6都是秩为12
#####t.reshape(2,2,3) 乘积仍是12


#例子 squeeze和unsqueeze

print(t.reshape(1,12))
print(t.reshape(1,12).shape)
#输出
tensor([[1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]])
torch.Size([1, 12])

#增加或者减少秩
#squeeze去掉维度是1的轴
print(t.reshape(1,12).squeeze())
print(t.reshape(1,12).squeeze().shape)
#输出:
tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.])
torch.Size([12])

#unsqueeze增加维度是1的轴
print(t.reshape(1,12).squeeze().unsqueeze(dim=0))
print(t.reshape(1,12).squeeze().unsqueeze(dim=0).shape)
#输出
tensor([[1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.]])
torch.Size([1, 12])

Flatten卷积层和全连接层
def flatten(t):
    t=t.reshape(1,-1)
    t=t.squeeze()
    return t
flatten(t)
#输出tensor([1., 1., 1., 1., 2., 2., 2., 2., 3., 3., 3., 3.])  -1表示任意值:根据reshape中元素个数以及已有的信息(这里是,其中一个维度为1),来计算机自动选一个合适的维度,并赋值给-1(在这里,运行过程中是12)
##非常大的区别,平铺之后tensor(【】),但是reshape(1,12)的表示是tensor(【【】】)。后者表示秩为2,但是前者为1

#cat来拼接??????????????????????????????????????

组合和平铺

t1 = torch.tensor([
    [1,1,1,1],
    [1,1,1,1],
    [1,1,1,1],
    [1,1,1,1]
])
t2 = torch.tensor([
    [2,2,2,2],
    [2,2,2,2],
    [2,2,2,2],
    [2,2,2,2]
])
t3=torch.tensor([
    [3,3,3,3],
    [3,3,3,3],
    [3,3,3,3],
    [3,3,3,3]
])
t=torch.stack((t1,t2,t3))
t.shape
t
#结果torch.Size([3, 4, 4])
#tensor([[[1, 1, 1, 1],
#         [1, 1, 1, 1],
#         [1, 1, 1, 1],
#         [1, 1, 1, 1]],
#
#        [[2, 2, 2, 2],
#         [2, 2, 2, 2],
#         [2, 2, 2, 2],
#         [2, 2, 2, 2]],
#
#        [[3, 3, 3, 3],
#         [3, 3, 3, 3],
#         [3, 3, 3, 3],
#         [3, 3, 3, 3]]])
t=t.reshape(3,1,4,4)
t
#结果
#tensor([         [       [                      [1, 1, 1, 1],
#       Batch      Channel  Height    width      [1, 1, 1, 1],
#                                                [1, 1, 1, 1],
#                                                [1, 1, 1, 1]]],
#
#
#        [[[2, 2, 2, 2],
#          [2, 2, 2, 2],
#          [2, 2, 2, 2],
#          [2, 2, 2, 2]]],
#
#
#        [[[3, 3, 3, 3],
#          [3, 3, 3, 3],
#          [3, 3, 3, 3],
#          [3, 3, 3, 3]]]])
#下标查看
t[0] #第一张照片
#输出tensor([[[1, 1, 1, 1],
#         [1, 1, 1, 1],
#         [1, 1, 1, 1],
#         [1, 1, 1, 1]]])
t[0][0] #第一个照片,第一个颜色
#输出tensor([[1, 1, 1, 1],
#          [1, 1, 1, 1],
#          [1, 1, 1, 1],
#          [1, 1, 1, 1]])
t[0][0][0] #第一个照片第一个颜色第一行
#输出tensor([1, 1, 1, 1])
t[0][0][0][0]
#输出tensor(1)


#flatten的四种操作
t.reshape(1,-1)[0]
t.reshape(-1)
t.view(t.numel())
t.flatten()
#输出为tensor([1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2, 2, 2, 2, 2, 2, 2, 2,
        2, 2, 2, 2, 2, 2, 2, 2, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3])
#最好的是用flatten

#选择性flatten
t.flatten(start_dim=1).shape  #【?。?。?。?】从四个里面选择索引为1的,开始进行操作。跳过批次,每张照片的颜色 宽 高进行faltten
t.flatten(start_dim=1)
#输出 torch.Size([3, 16])
#tensor([[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
#        [2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 2],
#        [3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3]])

元素操作element-wise operation

t1=torch.tensor([
    [1,2],
    [3,4]
],dtype=torch.float32)

t2=torch.tensor([
    [9,8],
    [7,6]
],dtype=torch.float32)
#有两个秩,第一个秩是array,第二个秩是数字

t1[0]
#输出tensor([1., 2.])
t1[0][0]
#tensor(1.)

#### two tensor must have the same shape,calculation is avaible = the same number of axes,and the lens of axes are the same
t1 + t2
tensor([[10., 10.],
        [10., 10.]])
其他的算法也都是元素操作

#四个基本张量-数字的操作(数字是0阶张量)
t1 + 2 == t1.add(2)
t1 - 2 == t1.sub(2)
t1 * 2 == t1.mul(2)
t1 / 2 == t1.div(2)
#输出为
tensor([[3., 4.],
        [5., 6.]])
tensor([[-1.,  0.],
        [ 1.,  2.]])
tensor([[2., 4.],
        [6., 8.]])
tensor([[0.5000, 1.0000],
        [1.5000, 2.0000]])

#对于上述过程的理解是 将2应用到新的形状t1.shape上面 成为broadcasting 广播
np.broadcast_to(2,t1.shape)
#结果array([[2, 2],
#       [2, 2]])
t1 + torch.tensor(
    np.broadcast_to(2,t1.shape)
    ,dtype=torch.float32
)
#结果是 tensor([[3., 4.],
#        [5., 6.]])

#复杂点的计算1张量和2张量
t1 = torch.tensor([
    [1,1],
    [1,1]
],dtype=torch.float32)
t2=torch.tensor([2,4],dtype=torch.float32)	
np.broadcast_to(t2.numpy(),t1.shape)
#结果array([[2., 4.],
#       [2., 4.]], dtype=float32)

#逻辑运算
t=torch.tensor([
    [0,5,7],
    [6,0,7],
    [0,8,0]
],dtype=torch.float32)
t.eq(0)
#结果tensor([[ True, False, False],
#        [False,  True, False],
#        [ True, False,  True]])
t.gt() lt() ge() le() eq() ne()代表大于 小于 大于等于 小于等于 等于 不等于
t.max(tensor) : 返回 tensor 中最大的一个数;
t.max(tt)     :  返回tt和t中每个元素tensor最大的数值
t.max(tensor,dim) : 指定维上最大数,返回 tensor 和下标;
t.max(tensor1,tensor2) : 比较两个 tensor 相比较大的元素;

#一些其他基本操作
t.abs()  #绝对值
t.sqrt() #平方根
t.neg()  #取反
t.neg().abs()  #取反之后取绝对值

张量缩减操作argmax

t=torch.tensor([
    [0,1,0],
    [2,0,2],
    [0,3,0]
],dtype=torch.float32)
t.sum()
t.numel()
t.sum().numel()
t.sum().numel()<t.numel()
#输出
#tensor(8.)
#9
#t.sum().numel()
#True
###sum operation是一个缩减命令
t.prod()乘积
t.mean()求平均值
t.std()求标准差
#都是缩减命令
t=torch.tensor([
    [1,1,1,1],
    [2,2,2,2],
    [3,3,3,3]
],dtype=torch.float32)
#这是一个秩为2,3*4的张量
t.sum(dim=0)
#输出 tensor([6.,6.,6.,6.,])
等价代码是
t[0] + t[1] + t[2]
 
t.sum(dim=1)
t[0].sum()
t[1].sum()
t[2].sum()

#输出 tensor([4.,8.,12.,])
##0是行维度,1是列维度:沿着第一个轴的是array,沿着第二个周的是number。sum(0)代表,把每个array相加得到的数值
t.max()  ##tensor(5.) 最大数是4
t.argmax()  ##tensor(11)  最大数值的index是11
t.flatten()  ##tensor([1., 0., 0., 2., 0., 3., 3., 0., 4., 0., 0., 5.])    argmax就是平铺之后的数值

t.max(dim=0)
#torch.return_types.max(
#values=tensor([4., 3., 3., 5.]),
#indices=tensor([2, 1, 1, 2]))          注意这里max返回两种数据
t.argmax(dim=0) 
#tensor([2, 1, 1, 2])             返回index
t.max(dim=1)
#torch.return_types.max(
#values=tensor([2., 3., 5.]),
#indices=tensor([3, 1, 3]))
t.argmax(dim=1)
#tensor([3, 1, 3])
t.mean()
#输出 tensor(1.5000)
t.mean().item()
#1.5    item命令将返回的mean提取为一个独立的元素

t.mean(dim=0).tolist()
#[1.6666666269302368, 1.0, 1.0, 2.3333332538604736]返回一个list
t.mean(dim=0).numpy()
#array([1.6666666, 1.       , 1.       , 2.3333333], dtype=float32)   返回一个numpy array

数据集

记住:

  • Who created the dataset?
  • How was the dataset created?
  • What transformations were used?
  • What intent does the dataset have ?
  • Possible unintentional consequences?
  • Is the dataset biased?
  • Are there ethical issues with the dataset?

项目计算的四个步骤

  • Prepare the data ETL过程 extract,transform,and load 两个重要的工具Dataset and Dataload
  • Build the model
  • Train the model
  • Analyze the model‘s results
#加载数据
import torch
import torchvision
import torchvision.transforms as transforms
train_set = torchvision.datasets.FashionMNIST(
    root='D:\\22SS\\cvhci\\FashionMNIST'             #数据的存储地址
    ,train=True                                      #数据的使用目的,这里是训练集
    ,download=True                                   #如果指定目录下面没有,那么可以下载
    ,transform=transforms.Compose([
        transforms.ToTensor()                        #因为我们想用这个数据集进行训练,所以我们ToTensor来将图片像素转化为张量
    ])
)
train_loader = torch.utils.data.DataLoader(train_set,batch_size=10)  #设定批大小为10

#数据展示
import numpy as np
import matplotlib.pyplot as plt
torch.set_printoptions(linewidth=120)   #设置打印到控制台的输出的行宽
len(train_set)                          #查看训练集中有多少图片 60000
train_set.train_labels                  #数据集的标签的张量tensor([9, 0, 0,  ..., 3, 0, 5]),在数据集中一共有10类
train_set.train_labels.bincount()       #数据集某个标签下的频率统计,展示所有标签的对应的频率tensor([6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000, 6000])

#单个图像输出
sample = next(iter(train_set))           #将train_set加入迭代器中,然后用next从头开始调用,一次next就调用一个
 len(sample)                            #输出为2,因为数据有一个标签张量一个图像内容张量
 type(sample)                           #这里是一个特殊的存储模式
 image,label = sample                   #这个是将两个张量赋值给两个数值,分别是image和label。可以把他理解为解构和封装
 image.shape                           #查看图片形状
 print(label)                          #查看标签
 plt.imshow(image.squeeze(),cmap="gray")
print("label:",label)                  #画出image,使用灰度图。然后写出标签

#多个图像输出
batch = next(iter(train_loader))          #输出2  
############ Dataloader 作为迭代器,每次产生一个 batch 大小的数据。但是用set就是每个图为单位
len(batch)                                #list
type(batch)
images,labels = batch
images.shape                              #torch.Size([10, 1, 28, 28])
labels.shape                              #torch.Size([10])
grid = torchvision.utils.make_grid(images,nrow=10)     #使用内置utils工具,以图片为单位,10列的形式生成网格
plt.figure(figsize=(15,15))                             #每个grid大小
plt.imshow(np.transpose(grid,(1,2,0)))                  #np转换成生成图片需要的结构

目标指向的NN

  • Extend the nn.Module base class
  • Define layers as class attributes
  • Implement the forward() method

使用nn.Module建立一个神经网络

class Network(nn.Module):               #将普通网络转化为pytorch nn.module所有功能的网络,各种内置参数都可以使用了     (权重更新更加方便)
    def __init__(self):                 #这里是双下划线
        super(Network,self).__init__()     #超参初始化

        self.conv1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6,out_channels=12,kernel_size=5)

        self.fc1 = nn.Linear(in_features=12*4*4,out_features=120)
        self.fc2 = nn.Linear(in_features=120,out_features=60)
        self.out = nn.Linear(in_features=60,out_features=10)

      
    def forward(self,t):
       #implement the forward pass
        return t
        
        
  network = Network()
  print(network)
  #输出Network(
  #(conv1): Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  #(conv2): Conv2d(6, 12, kernel_size=(5, 5), stride=(1, 1))
  #(fc1): Linear(in_features=192, out_features=120, bias=True)
  #(fc2): Linear(in_features=120, out_features=60, bias=True)
  #(out): Linear(in_features=60, out_features=10, bias=True)
#)
  network.conv1                                         #用这个.来识别各层网络的参数
  #输出Conv2d(1, 6, kernel_size=(5, 5), stride=(1, 1))
  
  network.conv1.weight
  network.conv1.weight.shape
  #查看某一层的权重和他的张量 torch.Size([6, 1, 5, 5])   6个filter 1个inputchannel 5*5的核大小 
  network.conv2.weight.shape
  #输出 torch.Size([12, 6, 5, 5])             #      output12 input6表示核的深度 filter 5*5
  network.conv2.weight[0].shape              #torch.Size([6, 5, 5])看一个filter的张量结构
  
  
  
  for param in network.parameters():
    print(param.shape)
  #输出为torch.Size([6, 1, 5, 5])
#torch.Size([6])
#torch.Size([12, 6, 5, 5])
#torch.Size([12])
#torch.Size([120, 192])
#torch.Size([120])
#torch.Size([60, 120])
#torch.Size([60])
#torch.Size([10, 60])
#torch.Size([10])

for name,param in network.named_parameters():             #\t\t是让结果对齐输出
    print(name,'\t\t',param.shape)
 #conv1.weight 		 torch.Size([6, 1, 5, 5])
#conv1.bias 		 torch.Size([6])
#conv2.weight 		 torch.Size([12, 6, 5, 5])
#conv2.bias 		 torch.Size([12])
#fc1.weight 		 torch.Size([120, 192])
#fc1.bias 		 torch.Size([120])
#fc2.weight 		 torch.Size([60, 120])
#fc2.bias 		 torch.Size([60])
#out.weight 		 torch.Size([10, 60])
#out.bias 		 torch.Size([10])

parameter是函数内部的参数,argument是函数使用者从外部给函数的数值

在cnn到fc层的时候,系统随即创建一个转换矩阵,然后进行转换。

in_features = torch.tensor([1,2,3,4],dtype = torch.float32)  #这里面in_feature是定义的函数,不是nn内置参数
weight_matrix = torch.tensor([
    [1,2,3,4],
    [2,3,4,5],
    [3,4,5,6]
],dtype = torch.float32)
weight_matrix.matmul(in_features)
#输出是 tensor([30., 40., 50.])
fc = nn.Linear(in_features=4,out_features=3)
fc(in_features)
#输出是tensor([ 0.6834, -0.7238, -0.1147], grad_fn=<AddBackward0>)  #这里展示的是系统会随机生成一个矩阵来计算

fc.weight = nn.Parameter(weight_matrix)  ###重点  将之前的矩阵赋值给fc层的矩阵
fc = nn.Linear(in_features=4,out_features=3)
fc(in_features)
#输出 tensor([29.5013, 40.2981, 49.9314], grad_fn=<AddBackward0>)很接近之前的值,他们不同因为增加了bias

#关闭bias之后的输出
fc.weight = nn.Parameter(weight_matrix)  
fc = nn.Linear(in_features=4,out_features=3,bias=False)
fc(in_features)
#tensor([0.5870, 2.8734, 0.8371], grad_fn=<SqueezeBackward3>)

前向传播

class Network(nn.Module):               #将普通网络转化为pytorch nn.module所有功能的网络,各种内置参数都可以使用了     (权重更新更加方便)
    def __init__(self):                 #这里是双下划线
        super(Network,self).__init__()     #超参初始化

        self.conv1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6,out_channels=12,kernel_size=5)

        self.fc1 = nn.Linear(in_features=12*4*4,out_features=120)
        self.fc2 = nn.Linear(in_features=120,out_features=60)
        self.out = nn.Linear(in_features=60,out_features=10)

      
    def forward(self,t):
        #implement the forward pass
        #inpuyt layer
        t = t

        #(2 hidden conv layer)

        t = self.conv1(t)
        t =  F.relu(t)
        t =  F.max_pool2d(t,kernel_size=2,stride=2)

        #(3 hidden conv layer)
        
        t = self.conv2(t)
        t =  F.relu(t)
        t =  F.max_pool2d(t,kernel_size=2,stride=2)

        #4 hidden liner layer

        t = t.reshape(-1,12*4*4)    #(4*4是之前卷积层计算的结果)
        t = self.fc1(t)
        t = F.relu(t)

        #5 hidden liner layer
        #  
        t = self.fc2(t)
        t = F.relu(t)

        #6 output layer
        t = self.out(t)
        #t=F.softmax(t,dim=1)

        
        return t
    
    ###关注他的书写顺序,conv activefunction pooling

图片预测,使用Pytorch

network = Network()       ####这一步的内在内容很重要:生成一个Instance然后在之后的调用过程中改变Instance的值。如果直接用Network()那么用一次创建一次Instance。Instance的创建占用额外的内存,要避免这种使用方法。
data_loader = torch.utils.data.DataLoader(
    train_set
    ,batch_size =10
)
batch = next(iter(data_loader))     #批次训练,单次训练见后面
images,labels = batch 
images.shape  
#输出 torch.Size([10, 1, 28, 28])
labels.shape  
#torch.Size([10])
preds = network(images)
preds.shape
#torch.Size([10, 10])
preds 
#tensor([[ 0.0844, -0.0992, -0.0125, -0.0030, -0.0452,  0.0589, -0.0883, #-0.1155,  0.0089, -0.1507],
#        [ 0.0879, -0.0960, -0.0179, -0.0092, -0.0378,  0.0620, -0.0946, #-0.1235,  0.0191, -0.1485],
#        [ 0.0883, -0.1031, -0.0143, -0.0102, -0.0397,  0.0586, -0.0922, #-0.1226,  0.0131, -0.1492],
#        [ 0.0897, -0.1030, -0.0179, -0.0089, -0.0390,  0.0566, -0.0930, #-0.1222,  0.0157, -0.1461],
#        [ 0.0877, -0.1058, -0.0149, -0.0106, -0.0444,  0.0574, -0.0943, #-0.1240,  0.0106, -0.1511],
#        [ 0.0864, -0.1053, -0.0125, -0.0046, -0.0422,  0.0592, -0.0920, #-0.1201,  0.0099, -0.1519],
#        [ 0.0786, -0.1043, -0.0049, -0.0073, -0.0423,  0.0721, -0.0916, #-0.1166,  0.0017, -0.1607],
#        [ 0.0901, -0.1058, -0.0089, -0.0053, -0.0373,  0.0598, -0.0950, #-0.1232,  0.0181, -0.1487],
#        [ 0.0840, -0.1002, -0.0178, -0.0031, -0.0400,  0.0589, -0.0870, #-0.1076,  0.0182, -0.1506],
#        [ 0.0831, -0.1011, -0.0167,  0.0025, -0.0463,  0.0546, -0.0763, #-0.1048,  0.0199, -0.1472]],
#       grad_fn=<AddmmBackward0>)
preds.argmax(dim=1) 
#tensor([0, 0, 0, 0, 0, 0, 0, 0, 0, 0])
labels
#tensor([9, 0, 0, 3, 0, 2, 7, 2, 5, 5])
eq = preds.argmax(dim=1).eq(labels)
preds.argmax(dim=1).eq(labels).sum()
#tensor(3)
#最后两行的等价函数
def get_num_correct(preds,labels):    #计算标签的正确数目
    return preds.argmax(dim=1).eq(labels).sum().item()
get_num_correct(preds,labels)
#3

#单次训练
sample = next(iter(train_set))
image,label = sample
output = network(image.unsqueeze(0))  #因为神经网络是[batch,channel,hight,weight],所以单个图片要加上一个1的batchsize。unsqueeze就是在index 0 的地方加上要给维度
print(output)
#tensor([[ 0.0844, -0.0992, -0.0125, -0.0030, -0.0452,  0.0589, -0.0883, -0.1155,  0.0089, -0.1507]],
       grad_fn=<AddmmBackward0>)

训练神经网络Traning

  1. Get batch from the training set
  2. Pass batch to network
  3. Calculate the loss(difference between the predicted values and the true values)
  4. Calculate the gradient of the loss function w.r.t the network’s weights
  5. Update the weights using the gradients to reduce the loss
  6. Repeat steps 1-5 until one epoch is completed
  7. Repeat steps 1-6 for as many epochs required to obtain the desired level of accuracy
#####------------单批次计算
network = Network()
train_loader = torch.utils.data.DataLoader(train_set,batch_size=100)            #数据加载,这里是载入图片,一百个图片是一个batch
optimizer = optim.Adam(network.parameters(),lr=0.01)                            #设置优化权重的方法

batch = next(iter(train_loader))                                               #用iter方法调用train_loader里面的一个batch数据,并且赋值给batch
images,labels = batch                                                          #图像处理的时候有两个维度,这里面我们将他分别赋值给images和labels

preds = network(images)                                                        #前向传播的images预测结果(初始预测,没有任何优化)
loss = F.cross_entropy(preds,labels)                                           #计算loss方程

loss.backward()                                                                #反向传播梯度
optimizer.step()                                                               #进行梯度优化

#第一次反向传播的loss计算
print('loss1:',loss.item())                                                   
preds = network(images)
#在输入一次,epoch第二次的梯度
loss = F.cross_entropy(preds,labels)
print('loss2',loss.item())

正确的网络书写顺序

#####------------单批次计算
network = Network()
train_loader = torch.utils.data.DataLoader(train_set,batch_size=100)            #数据加载,这里是载入图片,一百个图片是一个batch
optimizer = optim.Adam(network.parameters(),lr=0.01)                            #设置优化权重的方法

batch = next(iter(train_loader))                                               #用iter方法调用train_loader里面的一个batch数据,并且赋值给batch
images,labels = batch                                                          #图像处理的时候有两个维度,这里面我们将他分别赋值给images和labels

preds = network(images)                                                        #前向传播的images预测结果(初始预测,没有任何优化)
loss = F.cross_entropy(preds,labels)                                           #计算loss方程

loss.backward()                                                                #反向传播梯度
optimizer.step()                                                               #进行梯度优化

#第一次反向传播的loss计算
print('loss1:',loss.item())                                                   
preds = network(images)
#在输入一次,epoch第二次的梯度
loss = F.cross_entropy(preds,labels)
print('loss2',loss.item())

Training Loop

重点神经网络是批次batch训练,每个batch包括多张图片。当我们将训练集中所有图片都训练完一次之后,这是完成一个epoch

#加载包
import torch
import torch.nn as nn
import torch.nn.functional as F
import torch.optim as optim
import torchvision
import torchvision.transforms as transforms

torch.set_printoptions(linewidth=120)
torch.set_grad_enabled(True)

##计算标签的正确数目
def get_num_correct(preds,labels):    
    return preds.argmax(dim=1).eq(labels).sum().item()
    
#训练网络
class Network(nn.Module):               
    def __init__(self):                 
        super(Network,self).__init__()     

        self.conv1 = nn.Conv2d(in_channels=1,out_channels=6,kernel_size=5)
        self.conv2 = nn.Conv2d(in_channels=6,out_channels=12,kernel_size=5)

        self.fc1 = nn.Linear(in_features=12*4*4,out_features=120)
        self.fc2 = nn.Linear(in_features=120,out_features=60)
        self.out = nn.Linear(in_features=60,out_features=10)

      
    def forward(self,t):

        t = t

        t = self.conv1(t)
        t =  F.relu(t)
        t =  F.max_pool2d(t,kernel_size=2,stride=2)
 
        t = self.conv2(t)
        t =  F.relu(t)
        t =  F.max_pool2d(t,kernel_size=2,stride=2)

        t = t.reshape(-1,12*4*4)    
        t = self.fc1(t)
        t = F.relu(t)

        t = self.fc2(t)
        t = F.relu(t)

        t = self.out(t)
    
        return t
 
 
 #导入训练集
 train_set = torchvision.datasets.FashionMNIST(
    root='D:\\22SS\\cvhci\\FashionMNIST'             
    ,train=True                                      
    ,download=True                                   
    ,transform=transforms.Compose([
        transforms.ToTensor()                       
    ])
)



#----------------------------------训练一个epoch
#训练一个epoch
network = Network()
train_loader = torch.utils.data.DataLoader(train_set,batch_size=100)          
optimizer = optim.Adam(network.parameters(),lr=0.01)          

total_loss = 0
total_correct = 0

for batch in train_loader:                                            
    images,labels = batch                                                        

    preds = network(images)                                                       
    loss = F.cross_entropy(preds,labels)                                         

    optimizer.zero_grad()                    #每次都从0开始梯度,否则的话,他会每次都累加梯度。这并不是我们计算过程中想要的
    loss.backward()                                                          
    optimizer.step()   
    
    total_loss += loss.item()
    total_correct += get_num_correct(preds,labels)

print('epoch:',0,'total_correct:',total_correct,'loss',total_loss)




#-----------------------------------------------训练多个epoch
#训练5个epoch
network = Network()
train_loader = torch.utils.data.DataLoader(train_set,batch_size=100)          
optimizer = optim.Adam(network.parameters(),lr=0.01)          

for epoch in range(5):

    total_loss = 0
    total_correct = 0

    for batch in train_loader:                                            
        images,labels = batch                                                        

        preds = network(images)                                                       
        loss = F.cross_entropy(preds,labels)                                         

        optimizer.zero_grad()                    #每次都从0开始梯度,否则的话,他会每次都累加梯度。这并不是我们计算过程中想要的
        loss.backward()                                                          
        optimizer.step()   
        
        total_loss += loss.item()
        total_correct += get_num_correct(preds,labels)

    print('epoch:',epoch,'total_correct:',total_correct,'loss',total_loss)

Confusion matrix来分析神经网络训练结果

展示哪些类之间混淆了

关于是否进行梯度跟踪

len(train_set)
len(train_set.targets)
def get_all_preds(model,loader):
    all_preds = torch.tensor([])
    for batch in loader:
        images,labels = batch

        preds = model(images)
        all_preds = torch.cat(
            (all_preds,preds)
            ,dim=0
        )
    return all_preds
    
prediction_loader = torch.utils.data.DataLoader(train_set,batch_size=10000)
train_preds = get_all_preds(network,prediction_loader)

train_preds.shape
print(train_preds.requires_grad)   #表示这个训练集需要梯度跟踪
train_preds.grad
train_preds.grad_fn

with torch.no_grad(): ###如果在局部关闭梯度跟踪
    prediction_loader = torch.utils.data.DataLoader(train_set,batch_size=10000)
    train_preds=get_all_preds(network,prediction_loader)
    
print(train_preds.requires_grad)
train_preds.grad
train_preds.grad_fn

preds_correct = get_num_correct(train_preds,train_set.targets)
print('total correct',preds_correct)
print('accuracy',preds_correct/len(train_set))

confusion matrix

train_set.targets   #训练集自己的标签
train_preds.argmax(dim=1)   #训练集的预测标签
stacked = torch.stack(
    (
        train_set.targets
        ,train_preds.argmax(dim=1)
    )
    ,dim=1
)
stacked.shape
stacked 
stacked[0].tolist()
j,k = stacked[0].tolist()
j
k
cmt = torch.zeros(10,10,dtype=torch.int32)  #创建一个10*10的零矩阵
cmt
for p in stacked:     ###在真假矩阵里面,对应的块加一
    tl,pl = p.tolist()
    cmt[tl,pl] = cmt[tl,pl]+1
cmt

#plotting a confusion matrix

import matplotlib.pyplot as prediction_loader

from sklearn.matrics import confusion_matrix
from resources.pltcm import plot_confusion_matrix

cm = confusion_matrix(train_set.targets,train_preds.argmax(dim=1))
print(type(cm))
cm

names = ('T-shirt/top','trouser','pullover','4','5','6','7','8','9','10')
plt.figure(figsize=(10,10))
plot_confusion_matrix(cm,names)

结果


concatenating拼接 and stacking堆叠张量

拼接是在已有的轴上拼接张量

堆叠是在原来张量的基础上扩展新的轴

unsqueeze的使用

import torch
t1 = torch.tensor([1,1,1])
t1.shape
#结果torch.Size([3])
t1.unsqueeze(dim=0)
#结果tensor([[1, 1, 1]])
t1.unsqueeze(dim=1)
tensor([[1],
        [1],
        [1]])

#shape
print(t1.shape)
print(t1.unsqueeze(dim=0))
print(t1.unsqueeze(dim=1))

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

cat

t1=torch.tensor([1,1,1])
t2=torch.tensor([2,2,2])
t3=torch.tensor([4,3,3])
torch.cat(
    (t1,t2,t3)
    ,dim=0
)
#tensor([1, 1, 1, 2, 2, 2, 4, 3, 3])
torch.stack(
    (t1,t2,t3)
    ,dim=0
)
#tensor([[1, 1, 1],
#        [2, 2, 2],
 #       [4, 3, 3]])
  

torch.cat(
    (
        t1.unsqueeze(0)
        ,t2.unsqueeze(0)
        ,t3.unsqueeze(0)
        )
    ,dim=0
)
#输出tensor([[1, 1, 1],
#        [2, 2, 2],
#        [4, 3, 3]])
#在另外的维度上面,只能进行堆叠。因为本身t1,2,3没有第二个维度
torch.stack(
    (t1,t2,t3)
    ,dim=1
)
#tensor([[1, 2, 4],
#        [1, 2, 3],
#        [1, 2, 3]])

Tensorboard

tensorflow的可视化工具

打开tensorboard

(cvhci) C:\Users\tengf>tensorboard --logdir=runs
Serving TensorBoard on localhost; to expose to the network, use a proxy or pass --bind_all
TensorBoard 2.6.0 at http://localhost:6006/ (Press CTRL+C to quit)
###浏览器上面输入网址访问
(cvhci) C:\Users\tengf>tensorboard --version
2.6.0 
#查看tensorboard版本

#额外import一个tensorboard的支持
from torch.utils.tensorboard import SummaryWriter
####将原始图像images和labels传入tensorboard并且生成一个images的网格显示图片

network = Network()
images,labels = next(iter(train_loader))
grid = torchvision.utils.make_grid(images)     #后面要传给Summarywriter,去创建一组图像,

tb = SummaryWriter()
tb.add_image('images',grid)                    #传递标签和网格
tb.add_graph(network,images)
##在每批次for batch in train_loader中加入,传输给tensorboard每批次的信息
tb.add_scalar('Loss',total_loss,epoch)
tb.add_scalar('Number Correct',total_correct,epoch)
tb.add_scalar('Accuracy',total_correct/len(train_set),epoch)

tb.add_histogram('conv1.bias',network.conv1.bias,epoch)
tb.add_histogram('conv1.weight',network.conv1.weight,epoch)
tb.add_histogram('conv1.weight.grad',network.conv1.weight.grad,epoch)

Tensorboard重点:超参调试

如何建立多个超参,同时运行并且在tensorboard中展示


run Builder

from collections import OrderedDict
from collections import namedtuple
from itertools import product

class RunBuilder():
    @staticmethod
    def get_runs(params):

        Run = namedtuple('Run',params.keys())

        runs=[]
        for v in product(*params.values()):
            runs.append(Run(*v))

        return runs
     
 params = OrderedDict(                  #设置初始测试的各个参数
    lr = [.01,.001]
    ,batch_size = [1000,10000]
)

runs = RunBuilder.get_runs(params)     #调用runbuilder功能
runs
##结果是
#[Run(lr=0.01, batch_size=1000),
# Run(lr=0.01, batch_size=10000),
# Run(lr=0.001, batch_size=1000),
# Run(lr=0.001, batch_size=10000)]
run = runs[0]
print(run.lr,run.batch_size)
#0.01 1000

笛卡尔乘积就是两个集合x和y,乘积为(x,y)

RunManager

字典是另外一个可变的数据结构,且可存储任意类型对象,比如字符串、数字、列表等。字典是由关键字和值两部分组成,也就是 key 和 value,中间用冒号分隔。这种结构类似于新华字典,字典中每一个字都有一个对应的解释

_名字 这种表示内部定义,不能被外部引用

Num_Worker告诉程序用多少个单元去出去程序

减少从磁盘中提取数据的时间