!!!!!我省略掉了对于计算准确率,动画展示的部分
torchvision.transforms是pytorch中的图像预处理包,包含了很多种对图像数据进行变换的函数,这些都是在我们进行图像数据读入步骤中必不可少的.
-
torchvision.transforms.Compose():将多种变换组合在一起
-
transforms.ToTensor() 是将一个image格式转换成torch.FloatTensor的数据形式
-
transforms.Resize(resize)调整图片的风变绿
-
记录keepdim的作用
import numpy as np a = torch.ones((2,2)) b = np.array([[1,2,3],[1,1,1]]) c = torch.from_numpy(b) interval_0 = torch.sum(c, dim=0, keepdim=True) interval_1 = torch.sum(c, dim=1)
keepdim的作用可以防止计算完之后的数据变成一个一行的数据,第二个是防止维数被挤压
batch_size = 256
def get_dataloader_workers():
"""使用4个进程来读取数据。"""
return 2
#ok,现在我们整合所有的组件,并且定义函数用来获得数据集
def load_data_fashion_mnist(batch_size,resize=None):
#下载Fashion-Mnist数据集,然后将其加载到内存中
trans = [transforms.ToTensor()] #定义一个列表,这个列表中包含了想要对plmimage类型数据做的数据类型变换
if resize:
trans.insert(0,transforms.Resize(resize))#如果我们设定了resize,这里会重置它的分辨率
trans = transforms.Compose(trans)#组合这么这些个操作
mnist_train=torchvision.datasets.FashionMNIST(root="./data",train=True,transform=trans,download=True)#载入数据到内存
mnist_test=torchvision.datasets.FashionMNIST(root="./data",train=False,transform=trans,download=True)#载入数据到内存
return (data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_workers()),data.DataLoader
(mnist_test,batch_size,shuffle=False,num_workers=get_dataloader_workers()))
batch_size = 256
train_iter, test_iter = load_data_fashion_mnist(batch_size)
#初始化模型的参数
num_inputs = 784 #我们需要把一个28*28的图片拉长到1*784的向量,虽然这样会损失空间信息
num_outputs = 10 #最后我们的预测是只有10个分类的
W = torch.normal(0,0.01,size=(num_inputs,num_outputs),requires_grad=True)#定义我们的层,记录梯度
b = torch.zeros(num_outputs,requires_grad=True)#定义我们的bias,记录梯度
#定义softmax的操作
def softmax(X):
X_exp = torch.exp(X) #计算每一个参数求幂
partition = X_exp.sum(1,keepdim=True)
return X_exp/partition #注意这里应用了广播机制
对于这里的Softmax我有话要说
X = torch.normal(0,1,(2,5))#创建了一个均值为0,方差为1的,2行5列的矩阵
tensor([[ 1.3066, 0.0417, 0.6489, -0.0553, -0.9866],[-1.7921, -0.4884, 1.7815, 2.2112, -0.6010]])
X_exp = torch.exp(X),在这里,我们是对每一个元素,进行了以e为底的操作
partition = X_exp.sum(1,keepdim=True)
dim=1说明,最后我们的数据会变成一列数据,为什么要变成一列的数据,比如在这里就变成
partition = X_exp.sum(1,keepdim=True)
partition.shape
torch.Size([2, 1])
因为对于我们来说一行代表一个样本,我们需要再一行上进行分布的预测,tip在最后return的时候应用了广播操作,最后partition会变成[2*5]的矩阵,每一列的数值与第一列的数值相同
补充
tensor.reshape(-1)的意思。我们都知道,在我们进行矩阵的变换的时候,需要保持元素的数量是一致的。-1的意思就是定义了一个未知数,只需要会根据其他维数的信息,来计算这个维度
我在这里简单的形容一下交叉熵是如何计算的吧
重新起个头%matplotlib inline import torch import torchvision from IPython import display from torch.utils import data from torchvision import transforms from d2l import torch as d2l
这一部分是我们需要导入的库,我现在才发现,其实d2l库是李老师专门写出来的一个库,方便我们学习,同志们把泪目打在公屏上。
def get_fashion_mnist_labels(labels):
"""返回Fashion-MNIST数据集的文本标签。"""
text_labels = ['t-shirt', 'trouser', 'pullover', 'dress', 'coat',
'sandal', 'shirt', 'sneaker', 'bag', 'ankle boot']
return [text_labels[int(i)] for i in labels]
这个函数,我们会传入一个int类型的Labels,比如labels = [1,2,3],返回对应的文本标签
def get_dataloader_workers():
"""使用4个进程来读取数据。"""
return 4
并行的读取数据,这个部分可以根据自己的电脑情况调大调小
def load_data_fashion_mnist(batch_size, resize=None):
"""下载Fashion-MNIST数据集,然后将其加载到内存中。"""
trans = [transforms.ToTensor()] # 在这里,我们规定了数据会进行什么变换
if resize: #如果resize这个参数不为0我们就会重置它的分辨率
trans.insert(0, transforms.Resize(resize)) #进行Resize函数,重置分辨力
trans = transforms.Compose(trans) #把这一系列的操作组合在一起
mnist_train = torchvision.datasets.FashionMNIST( #获取训练数据集
root="./data", train=True, transform=trans, download=True)
mnist_test = torchvision.datasets.FashionMNIST( #获取测试数据集
root="./data", train=False, transform=trans, download=True)
return (data.DataLoader(mnist_train, batch_size, shuffle=True,
num_workers=get_dataloader_workers()), # 在pytorch中,使用DataLoader是一件毋庸置疑的事情
data.DataLoader(mnist_test, batch_size, shuffle=False,
num_workers=get_dataloader_workers()))
注意,我们需要对数据进行一系列的操作,trans就可以理解为这些操作的一个集合
resize就是重新定义它的分辨率
在pytorch中,我们的一切数据都需要分装成dataLoader,给定一个数据集,指定batchsize,在指定是否打乱,指定几个进程去读。这个都是用的很多,就不多说了
def softmax(X):
X_exp = torch.exp(X) #首先对于输入的的X,可能是一个向量,可能是一个矩阵,进行指数的求解
partition = X_exp.sum(1, keepdim=True) #我们现在是消灭列这个维数,为什么,因为对我们的设定,每一行是一个样本,所以要求的是行和而不是列和
return X_exp / partition # 这里应用了广播机制
就举个我们本例来说
我们的输入是
X是一个[256784]的矩阵W是一个[78410]的一个矩阵,就是一个[25610]的矩阵,我们是在一个[25610]的矩阵上进行一个softmax操作。
在我们本例上,我们的一行代表我们的一个样本,所以我们需要的是一个行和,而不是列和。
最后应用到了广播机制,当我们的使用完求和之后,获得的是一个[2561]的矩阵,需要扩展到
一个[256*10]的矩阵。
def net(X):
return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b) #reshape -1参数的意思是,这个位置的大小是根据其他维数算出来的
#在这里我们提前把X的维度从一个256*1*28*28的维度拉长到了一个256*784的维度
在这里定义我们的网络层,我们的网络层可以简单理解一个一个线性映射加上一个softmax
def cross_entropy(y_hat, y):
return - torch.log(y_hat[range(len(y_hat)), y]) #在这里定义我们的损失函数,我们只需要知道,对于分类问题,我们大部分时间都是使用交叉熵这个损失函数的```
定义我们的损失函数,使用的是交叉熵,具体公式以后自己去理解。
但是我只能说
y_hat是一个256 10的矩阵
y的维度是一个2561的矩阵
def train_epoch_ch3(net, train_iter, loss, updater):#真正开始训练的函数
"""训练模型一个迭代周期(定义见第3章)。"""
# 将模型设置为训练模式
#net就是我们创建好的网络,在这里,就是一个softmax的全连接层
#train_iter就是我们的训练数据集
#loss就是我们定义的损失函数
#updater就是sgd优化函数,很简单,就是在梯度方向每一个参数走一个lr学习率的值
if isinstance(net, torch.nn.Module):#首先将这个网络的模式,设置成训练模式,就是开启了梯度记录,梯度的计算
net.train()
# 训练损失总和、训练准确度总和、样本数
metric = Accumulator(3) #创建一个累加器
for X, y in train_iter:
#根据我们的定义,每一次我们都会从数据中读取一个256张图片已经对应的label
# X的维度是 256*1*28*28
# y的维度是 256*1
# 计算梯度并更新参数
y_hat = net(X) #计算预测值,其实这里出来的还是一个256*10的一个矩阵
l = loss(y_hat, y) #计算损失,这里使用的是交叉熵来当损失函数
if isinstance(updater, torch.optim.Optimizer):
# 使用PyTorch内置的优化器和损失函数,这是使用了pytorch带的api的情况
updater.zero_grad() #清空梯度,不然会在原来的梯度上累计叠加
l.backward()#计算梯度
updater.step()#拿计算出来的梯度更新模型
metric.add(float(l) * len(y), accuracy(y_hat, y),
y.size().numel())#开始add
else:
# 使用定制的优化器和损失函数
l.sum().backward()#如果我们使用的是自己定义的方式呢,又是另外一种情况
updater(X.shape[0]) #更新参数
metric.add(float(l.sum()), accuracy(y_hat, y), y.numel())#计算
# 返回训练损失和训练准确率
return metric[0] / metric[2], metric[1] / metric[2] #返回损失和准去率
记住我们的步骤,一定是先计算损失,然后根据损失计算出来的损失调用 backwar()方向传播函数,然后再更新模型一定是这么一个操作
def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
"""训练模型(定义见第3章)。"""
animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
legend=['train loss', 'train acc', 'test acc']) #这一部分是动画函数,我们可以暂且放一放
for epoch in range(num_epochs): #根据我们规定的训练轮回,进行一个迭代
train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
#net就是我们创建好的网络,在这里,就是一个softmax的全连接层
#train_iter就是我们的训练数据集
#loss就是我们定义的损失函数
#updater就是sgd优化函数,很简单,就是在梯度方向每一个参数走一个lr学习率的值
test_acc = evaluate_accuracy(net, test_iter)
animator.add(epoch + 1, train_metrics + (test_acc,))
train_loss, train_acc = train_metrics
assert train_loss < 0.5, train_loss
assert train_acc <= 1 and train_acc > 0.7, train_acc
assert test_acc <= 1 and test_acc > 0.7, test_acc
def updater(batch_size):
return d2l.sgd([W, b], lr, batch_size)
#这个sgd就是一个优化更新的方法,对于每一个参数,进行一个 学习率lr与梯度方向上的优化
batch_size = 256 #在这里我们初始化一个batch_size的大小
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size) #在这里我们先获得我们的训练集,和我们的测试集
for X, y in train_iter:
print(X.reshape(-1, 784).shape)
break
num_inputs = 784
num_outputs = 10
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True) #创建一个均值为0,方差为0.01的数据集,可以理解为他是一个输入784维,输出是10维的神经网络,并且开启梯度记录
b = torch.zeros(num_outputs, requires_grad=True) #创建我们的bias,并且开启梯度记录
num_epochs = 10
train_ch3(net, train_iter, test_iter, cross_entropy, num_epochs, updater) #理论上这样就可以开始跑了
如果使用pytorch的api呢?
batch_size = 256
train_iter,test_iter = d2l.load_data_fashion_mnist(batch_size) #别的不多说了,这个数据初始化,还是要自己初始化的呀宝儿
net = nn.Sequential(nn.Flatten(),nn.Linear(784,10))# 在这里nn.Flatten就是把数据磨平,默认从维度1还是这个和torch.flatten()不一样
def init_weight(m):
if type(m) == nn.Linear:
nn.init.normal_(m.weight,std=0.01) #我们开始定义我们的参数是如何呗初始化的
net.apply(init_weights)#初始化我们的函数
loss = nn.CrossEntropyLoss()#以后直接调用我们的api,这就是我们的损失函数
trainer = torch.optim.SGD(net.parameters(), lr=0.1)#这个就是我们的优化函数
num_epochs = 10#定义优化的论述
d2l.train_ch3(net, train_iter, test_iter, loss, num_epochs, trainer)#开始训练,就是如此的简单优雅
从这里我们能看到所谓的 LInear本质是就是矩阵的相乘,所谓的神经网络就是这么一个一个的线性变化并没有什么难度。
并且在这里我也要提出,优化函数是决定我们那里更新的梯度和学习率之后应该做的实行,backward()是反向传播,计算梯度,而loss是规定我们的损失函数的。这是一个逆向的过程。正向的过程是,先计算损失函数,因为开启了训练模式,会记录梯度,然后根据backward()计算梯度,并且记录,然后用优化函数去更新参数,以上!!!!很经典的一套过程



