依旧使用了数据集MNIST中的部分数据。
1、PyTorch基本知识
import torch
#简单的PyTorch张量
x=torch.tensor(3.5)
print(x)
#变量x的类型是PyTorch张量
tensor(3.5000)输出值的含义是,该变量的值是3.5000,同时它被包装在一个PyTorch张量中。
#简单的张量计算
y=x+3
print(y)
#生成的y也是一个PyTorch张量
x=torch.tensor(3.5,requires_grad=True)
#通过x的参数requires_grad=True告诉PyTorch我们希望得到一个关于x的梯度
print(x)
y=(x-1)*(x-2)*(x-3)
print(y)
#计算梯度
y.backward()
#x=3.5时的梯度
x.grad
#创建包含x y和z的计算图
x=torch.tensor(3.5,requires_grad=True)
y=x*x
z=2*y+3
#计算梯度
z.backward()
#当x=3.5时的梯度
x.grad
#创建包含x y和z的计算图
a=torch.tensor(2.0,requires_grad=True)
b=torch.tensor(1.0,requires_grad=True)
x=2*a+3*b
y=5*a*a+3*b*b*b
z=2*x+3*y
#计算梯度
z.backward()
#当a=2.0时的梯度
a.grad
#导入pandas库,用于读取csv文件
import pandas
#使用pandas将训练数据读取到一个DataFrame中
df=pandas.read_csv('mnist_train.csv',header=None)
#使用head()函数查看一个较大的DataFrame的前几行
df.head()
#使用info()函数查看DataFrame的概况
df.info()
以上结果告诉我们,该DataFrame有200行,这对应200幅训练图像,同时,每行有785个值。
#导入pandas库用于读取csv文件
import pandas
#导入matplotlib用于绘图
import matplotlib.pyplot as plt
#从DataFrame读取数据
row=0
#row=0表示第一行
data=df.iloc[row]
#选择数据集的第一行并赋值给变量data
#第一个值是标签
label=data[0]
#图像是余下的784个值
img=data[1:].values.reshape(28,28)
#选取剩余的784个值,将他们重新映射成为一个28x28的正方形数组,并赋值给变量img
plt.title("label="+str(label))
#在标题显示之前提取的标签
plt.imshow(img,interpolation='none',cmap='Blues')
#pyplot无须平滑像素以及调色板的颜色为蓝色
plt.show()
2、全部代码
2.1 导入包
import numpy
import torch
import torch.nn as nn
#将torch.nn模块作为nn导入,是一种常见的命名方式
from torch.utils.data import Dataset
#将数据载入torch.utils.data.Dataset
import pandas
#导入pandas库用于读取csv文件
import matplotlib.pyplot as plt
#导入matplotlib用于绘图
2.2 神经网络类
class Classifier(nn.Module):
#Classifier类继承了nn.Module
def __init__(self):
#__init__(self)是一个特殊的函数,当我们从一个类最终创建对象时需要调用它,它也被称为构造函数
#初始化PyTorch父类,调用父类构造函数
super().__init__()
#定义神经网络层
self.model=nn.Sequential(
#nn.Sequential()提供了一个神经模块的列表
nn.Linear(784,200),
#nn.Linear(784,200)是一个从784个节点到200个节点的全连接映射。
这个模块包含节点之间链接的权重,在训练时会被更新
#nn.Sigmoid(),
#nn.Sigmoid()将S型逻辑激活函数应用于前一个模块的输出,也就是本例中200个节点的输出
nn.LeakyReLU(0.02),
#改进:将激活函数改为LeakReLU(0.02),其中0.02是函数左半边的梯度
nn.LayerNorm(200),
#标准化,减少神经网络中参数和信号的取值范围,以及将均值转换为0
#在网络信号输出最终层之前使用nn.LayerNorm(200)将它们标准化
nn.Linear(200,10),
#nn.Linear(200,10)是将200个节点映射到10个节点的全连接映射,它包含中间隐藏层与输出层10个节点之间链接的权重
nn.Sigmoid()
#nn.Sigmoid()再将S型逻辑激活函数应用于10个节点的输出。
其结果就是网络的最终输出
#nn.LeakyReLU(0.02)
#改进:将激活函数改为LeakReLU(0.02),其中0.02是函数左半边的梯度
)
#nn.Linear:当数值从输入端传递到输出端时,该模块对它们应用了Ax+B形式的线性函数。
这里,A为链接权重,B为偏差。
这两个参数都会在训练时被更新。
它们也被称为可学习参数。
#创建损失函数
#self.loss_function=nn.MSELoss()
#均方误差:先计算每个输出节点的实际输出和预期输出之差的平方,再计算平均值,即torch.nn.MSELoss()
#适合情况:回归任务,输入值设计为连续范围的值,如,一个预测温度的网络会输出0~100°C的任何值
self.loss_function=nn.BCELoss()
#改进:对图像进行分类,分类任务(判断true/false或者0/1),常用二元交叉熵损失,它同时惩罚置信度高的错误输出和置信值低的正确输出,即nn.BCELoss()
#但是BCE只能处理0~1的值,而Leaky ReLU有可能输出范围之外的值,则,可以在最终层之后保留一个S型函数,但是在隐藏层之后使用LeakyReLU
#创建优化器,使用简单的梯度下降
#self.optimiser=torch.optim.SGD(self.parameters(),lr=0.01)
#随机梯度下降(SGD),将学习率设置为0.01
#在上面的代码中,我们通过self.parameters()访问所有可学习参数,并将所有可学习参数都传递给SGD优化器
self.optimiser=torch.optim.Adam(self.parameters())
#随机梯度下降的缺点:会陷入损失函数的局部最小值;对所有可学习的参数都使用单一的学习率
#改进:Adam,利用动量的概念,减小陷入局部最小值的可能性,想象一下:一个沉重的球如何利用动量滚过一个小坑;对每个可学习参数使用单独的学习率,这些学习率随着每个参数在训练期间的变化而变化
#可视化训练,跟踪训练的一种方法是监控损失
#记录训练进展的计数器和列表
self.counter=0
self.progress=[]
pass
def forward(self,inputs):
#通过forward()方法向网络传递信息
#直接运行模型
return self.model(inputs)
#将输入传递给self.model(),它由nn.Sequential()定义,模型的输出直接返回给forward()的主调函数
def train(self,inputs,targets):
#train()既需要网络的输出值,也需要预期的目标值,这样才可以与实际输出进行比较,并计算损失值
#计算网络的输出值
outputs=self.forward(inputs)
#计算损失值
loss=self.loss_function(outputs,targets)
#使用损失来更新网络的链接权重
#梯度归零,反向传播,并更新权重
self.optimiser.zero_grad()
#optimiser.zero_grad()将计算图中的梯度全部归零
loss.backward()
#loss.backward()从loss函数中计算网络中的梯度
#每次训练网络之前,需要将梯度归零,否则,每次loss.backward()计算出来的梯度会累积
self.optimiser.step()
#optimiser.step()使用这些梯度来更新网络的可学习参数
#每隔10个训练样本增加一次计数器的值,并将损失值添加进列表的末尾
self.counter+=1
if(self.counter%10==0):
self.progress.append(loss.item())
#item()函数是为了方便展开一个单值张量,获取里面的数字
pass
if(self.counter%1000==0):
#每1000次训练后打印计数器的值,这样可以了解训练进展的快慢
print("counter=",self.counter)
pass
def plot_progress(self):
#将损失值绘制成图
df=pandas.DataFrame(self.progress,columns=['loss'])
#将损失值列表progress转换为一个pandas DataFrame,便于绘图
df.plot(ylim=(0,1.0),figsize=(16,8),alpha=0.1,marker='.',grid=True,yticks=(0,0.25,0.5))
#使用plot()函数的选项,设计图的设计和风格
pass
2.3 数据集类
class MnistDataset(Dataset):
def __init__(self,csv_file):
self.data_df=pandas.read_csv(csv_file,header=None)
#csv_file被读入一个名为data_df的pandas DataFrame中
pass
def __len__(self):
#__len__()返回数据集中的项目总数
return len(self.data_df)
#通过len(mnist_dataset)获取数据集的大小
def __getitem__(self,index):
#__getitem__()通过所以返回数据集中的第n项,例如使用mnist_dataset[3]访问第4项
#目标图像(标签)
label=self.data_df.iloc[index,0]
#第index项中提取标签label
target=torch.zeros((10))
#创建维度为10的张量变量target来表示神经网络的预期输出
target[label]=1.0
#除了与标签相对应的项是1之外,其他值皆为0,这种表示方法叫做独热编码
#比如,标签0所对应的张量是[1,0,0,0,0,0,0,0,0,0],标签4所对应的张量是[0,0,0,0,1,0,0,0,0,0]
#图像数据,取值范围是0~255,标准化为0~1
image_values=torch.FloatTensor(self.data_df.iloc[index,1:].values)/255.0
#返回标签、图像数据张量以及目标张量
return label,image_values,target
def plot_image(self,index):
#绘图
arr=self.data_df.iloc[index,1:].values.reshape(28,28)
plt.title("label="+str(self.data_df.iloc[index,0]))
plt.imshow(arr,interpolation='none',cmap='Blues')
pass
2.4 使用训练数据训练神经网络
mnist_dataset=MnistDataset('mnist_train.csv')
#导入训练数据mnist_train.csv
mnist_dataset.plot_image(9)
#绘制第10幅图像,第10幅图像的索引是9,因为第一幅图像的索引是0
mnist_dataset[9]
%%time
#%%time可以用来记录本单元格运行所需时间
#创建神经网络
C=Classifier()
#在MNIST数据集训练神经网络
epochs=3
for i in range(epochs):
print('training epoch',i+1,"of",epochs)
for label,image_data_tensor,target_tensor in mnist_dataset:
C.train(image_data_tensor,target_tensor)
pass
pass
#绘制分类器损失值
C.plot_progress()
#损失值得下降意味着网络分类图像能力越来越好
#生成的图每次都会有点区别,因为神经网络本质是一个随机过程
#可以看出,改进之后的网络,损失值下降的速率要高于原始网络(如果我们考虑噪声在图中的密度,而不考虑高度,则损失值得噪声也比较少)
2.5 测试数据
#加载MNIST测试数据
mnist_test_dataset=MnistDataset('mnist_test.csv')
#挑选一幅图像,选择索引为19的第20幅图像
record=19
#绘制图像和标签
mnist_test_dataset.plot_image(record)
#接下来看看神经网络如何判断这副图像
image_data=mnist_test_dataset[record][1]
#调用训练后的神经网络
output=C.forward(image_data)
#绘制输出张量
pandas.DataFrame(output.detach().numpy()).plot(kind='bar',legend=False,ylim=(0,1))
#输出被转换成一个简单的numpy数组,再被包装成一个DataFrame,以便绘制柱形图
#10条柱形分别对应10个神经网络输出节点的值,最大值对应4,也就是说我们的网络认为图像是4
#测试用训练数据训练后的网络
score=0
items=0
for label,image_data_tensor,target_tensor in mnist_test_dataset:
answer=C.forward(image_data_tensor).detach().numpy()
if(answer.argmax()==label):
#answer.argmax()语句的作用是输出张量answer中最大值的索引。
如果第一个值是最大的,则argmax是0
score+=1
pass
items+=1
pass
print(score,items,score/items)
准确率96%,感觉挺好~
欢迎分享,转载请注明来源:内存溢出
评论列表(0条)