完整代码:
import torch
from d2l import torch as d2l
"创建训练集&创建检测集合"
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
"每个图片长度,以及图片种类"
num_inputs = 784
num_outputs = 10
"模型全局"
W = torch.normal(0, 0.01, size=(num_inputs, num_outputs), requires_grad=True)
b = torch.zeros(num_outputs, requires_grad=True)
"softmax全局"
def softmax(X):
X_exp = torch.exp(X)
partition = X_exp.sum(1, keepdim=True) # 计算行的和
return X_exp / partition # 这里应用了广播机制
"输出,即传入图片输出"
def net(X):
return softmax(torch.matmul(X.reshape((-1, W.shape[0])), W) + b)
"交叉熵损失"
def cross_entropy(y_hat, y):
return - torch.log(y_hat[range(len(y_hat)), y])
"显示预测与估计相对应下标数量"
def accuracy(y_hat, y): #@save
"""计算预测正确的数量"""
if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # 确定长宽高都大于1
y_hat = y_hat.argmax(axis=1) # 取出每行中最大值
cmp = y_hat.type(y.dtype) == y
return float(cmp.type(y.dtype).sum()) # 返回对应下标数量
"利用优化后的模型计算精度"
def evaluate_accuracy(net, data_iter): #@save
if isinstance(net, torch.nn.Module):
net.eval() # 将模型设置为评估模式
metric = Accumulator(2) # 正确预测数、预测总数
with torch.no_grad():
for X, y in data_iter:
metric.add(accuracy(net(X), y), y.numel()) # 下标相同数量 / 总下标
return metric[0] / metric[1]
"加法器全局"
class Accumulator: #@save
def __init__(self, n):
self.data = [0.0] * n
def add(self, *args):
self.data = [a + float(b) for a, b in zip(self.data, args)]
def reset(self):
self.data = [0.0] * len(self.data)
def __getitem__(self, idx):
return self.data[idx]
"训练更新模型&返回训练损失与精度函数"
def train_epoch_ch3(net, train_iter, loss, updater): #@save
"""训练模型一个迭代周期(定义见第3章)"""
# 将模型设置为训练模式
if isinstance(net, torch.nn.Module):
net.train()
# 训练损失总和、训练准确度总和、样本数
metric = Accumulator(3)
for X, y in train_iter:
# 计算梯度并更新参数
# print(y)
y_hat = net(X)
l = loss(y_hat, y)
if isinstance(updater, torch.optim.Optimizer):
# 使用PyTorch内置的优化器和损失函数
updater.zero_grad()
l.mean().backward()
updater.step()
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]
lr = 0.1
"更新模型"
def updater(batch_size):
return d2l.sgd([W, b], lr, batch_size)
if __name__ == '__main__':
num_epochs = 10
cnt = 1
print(W)
print(b)
print(W.shape)
for i in range(num_epochs):
X, Y = train_epoch_ch3(net, train_iter, cross_entropy, updater)
print("训练次数: " + str(cnt))
cnt += 1
print("训练损失: {:.4f}".format(X)) #训练损失是用训练数据集测得
print("训练精度: {:.4f}".format(evaluate_accuracy(net, test_iter))) # 训练精度是用测试数据集测得
print(".................................")
print(W)
print(b)
Fashion MNIST数据集中,每个样本都属于10个不同的类别,代表不同类型的服装(如鞋子、衬衫、裤子等)虽然只有10种类别但是每种类别有很多个样例,足够我们训练模型以及监测模型,所以这里输出为10,每次获取256个样例(获取的256个样例中有多种类别,鞋子,衬衫等,并不是单一类别)且返回的除了样例之外,还有该样例的标号(这个标号是为了区分该样例到底是什么)后续会讲到
还有一点就是获取的图片的像素是28*28大小,而且是二维,每个像素范围是0~255表示不同的颜色,这个数据迭代器load_data_fashion_mnist会将数据预处理成784,也就是一个维度
这种处理方式的优点就是更方便计算,但是缺点就是损失了一些空间信息,所以训练出来的模型算是低级模型吧,更高级的模型还是要保留图片二维空间训练出来
"创建训练集&创建检测集合"
batch_size = 256
train_iter, test_iter = d2l.load_data_fashion_mnist(batch_size)
"每个图片长度,以及图片种类"
num_inputs = 784
num_outputs = 10
这里sgd是库函数提供的更新梯度函数,由于梯度是由python自动帮你计算,他会在updater(X.shape[0])后自动获取计算的梯度
"更新模型"
def updater(batch_size):
return d2l.sgd([W, b], lr, batch_size)
这个函数就是查看模型的识别情况,看识别对的数量有多少
"显示预测与估计相对应下标数量"
def accuracy(y_hat, y): #@save
"""计算预测正确的数量"""
if len(y_hat.shape) > 1 and y_hat.shape[1] > 1: # 确定长宽高都大于1
y_hat = y_hat.argmax(axis=1) # 取出每行中最大值
cmp = y_hat.type(y.dtype) == y
return float(cmp.type(y.dtype).sum()) # 返回对应下标数量
这个函数就是对前面生成的数据以及函数的调用,并检测生成的模型的情况
"训练更新模型&返回训练损失与精度函数"
def train_epoch_ch3(net, train_iter, loss, updater): #@save
"""训练模型一个迭代周期(定义见第3章)"""
# 将模型设置为训练模式
if isinstance(net, torch.nn.Module):
net.train()
# 训练损失总和、训练准确度总和、样本数
metric = Accumulator(3)
for X, y in train_iter:
# 计算梯度并更新参数
# print(y)
y_hat = net(X)
l = loss(y_hat, y)
if isinstance(updater, torch.optim.Optimizer):
# 使用PyTorch内置的优化器和损失函数
updater.zero_grad()
l.mean().backward()
updater.step()
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]
至于损失函数以及softmax就不再赘述,前面的文章已经剖析得很清楚了
深度学习_8_对Softmax回归的理解
以下主要讲述对求y_hat以及梯度计算得理解:
由上述可知,获取的数据集的形状train_iter, test_iter它们的形状为(256, 784)
模型W的形状为(784, 10)
模型b的形状为(10, )
为了方便不考虑波动值b
为了模拟计算过程我们将数据进行相对缩小
784 ----> 4
256 ----> 3
10 ----> 2
那么获取的数据集合的shape就是(3, 4)设获取得数据集为X假设其具体值为
[
[1, 2, 3, 4],
[5, 6, 7, 8,],
[9, 10, 11, 12]
]
那么模型W的形状就是(4, 2)其为
[
[w1,w2],
[w3,w4],
[w5,w6],
[w7,w8]
]
X * W是可行的其结果为:
[
[(1*w1+2*w3+3*w5+4*w7), (1*w2+2*w4+3*w6+4*w8)],
[(5*w1+6*w3+7*w5+8*w7), (5*w2+6*w4+7*w6+8*w8)],
[(9*w1+10*w3+11*w5+12*w7), (9*w2+10*w4+11*w6+12*w8)],
]
其中w1,w2…有对应数值,并不是单纯未知数
这里X*W的每个元素值对应Oi,要将Oi转成y_hat,进行一次softmax操作即可
展示这个的原因只是想证实y_hat由W中的元素w1,w2,w3…构成,那么后面求得的损失函数也是由w1,w2,w3…构成
而求梯度的操作就是对损失函数求每个wi偏导的操作
W的shape是(4,2)其中的每个元素都是wi,所以最后的求得得梯度也是(4, 2),最后有W和梯度以及学习率就可以更新新的W了
最后理解一下偏导:
偏导将偏导得变量当作未知数,其他变量当作常数,是因为本次求导最关心得是本变量对整个函数的数值影响,而忽略其他变量对函数的影响