softmax回归一般实现

从回归到多类分类-均方损失,校验比例

输出匹配概率(非负,和为1)

交叉熵可以用来衡量两个概率之间的区别

从回归到多类分类—校验比例

  1. %matplotlib inline
  2. import torch
  3. import torchvision
  4. from torch.utils import data
  5. from torchvision import transforms
  6. from d2l import torch as d2l
  1. #数据下载
  2. trans=transforms.ToTensor()
  3. mnist_train=torchvision.datasets.FashionMNIST(root='../data',train=True,transform=trans,download=True)
  4. mnist_test=torchvision.datasets.FashionMNIST(root='../data',train=False,transform=trans,download=True)
  1. len(mnist_train),len(mnist_test)
  2. mnist_train[0][0].shape

可视化数据集

  1. def show_images(imgs, num_rows, num_cols, titles=None, scale=1.5): #@save
  2. """绘制一系列图片"""
  3. figsize = (num_cols * scale, num_rows * scale)
  4. _, axes = d2l.plt.subplots(num_rows, num_cols, figsize=figsize)
  5. axes = axes.flatten()
  6. for i, (ax, img) in enumerate(zip(axes, imgs)):
  7. if torch.is_tensor(img):
  8. # 图片张量
  9. ax.imshow(img.numpy())
  10. else:
  11. # PIL图片
  12. ax.imshow(img)
  13. ax.axes.get_xaxis().set_visible(False)
  14. ax.axes.get_yaxis().set_visible(False)
  15. if titles:
  16. ax.set_title(titles[i])
  17. return axes
  1. X, y = next(iter(data.DataLoader(mnist_train, batch_size=18)))
  2. show_images(X.reshape(18, 28, 28), 2, 9, titles=get_fashion_mnist_labels(y));#

image.png

  1. #读取一小批量数据,大小为batch_size
  2. batch_size=256
  3. def get_dataloader_workers():
  4. """使用4个进程来读取数据"""
  5. return 4
  6. train_iter=data.DataLoader(mnist_train,batch_size,shuffle=True,num_workers=get_dataloader_workers())
  7. timer=d2l.Timer()#计算运算时间
  8. for X,y in train_iter:
  9. continue
  10. f'{timer.stop():.2f} sec'
  1. import torch
  2. from IPython import display
  3. from d2l import torch as d2l
  4. batch_size=256
  5. train_iter,test_iter=d2l.load_data_fashion_mnist(batch_size)
  1. #初始化权重和偏倚
  2. #将展平每个图像,将它们视为784的向量。因为我们的数据集有10个类别,所以网络输出维度为10
  3. num_inputs=784
  4. num_outputs=10
  5. #首先定义权重w,这是一个正太分布的值,均值为0,方差为0.1,然后矩阵的大小为(784,10),需要计算梯度
  6. w=torch.normal(0,0.01,size=(num_inputs,num_outputs),requires_grad=True)
  7. #对于每个输出都要有一个偏倚,一共有十个输出,先确定为0
  8. b=torch.zeros(num_outputs,requires_grad=True)
  1. #给定一个矩阵X,可以对所有元素求和,这里只是一个简单举例
  2. X=torch.tensor([[1,2,3],[4,5,6]])
  3. X.sum(0,keepdim=True),X.sum(1,keepdim=True)
  1. #使用pytorch实现softmax
  2. def softmax(X):
  3. X_exp=torch.exp(X) #首先对每个向量进行指数的计算
  4. partition=X_exp.sum(1,keepdim=True) #然后按照维度1进行求和,也就是每一行所有的数字都加起来
  5. return X_exp/partition #这里使用的广播的机制,也就是对于每一行的每一个数字都用和除以一下,和R中的sweep是一样的
  1. #将每个元素都变成一个非负数,此外,根据概率原理,每一行的总和都是1
  2. X=torch.normal(0,1,(2,5))
  3. X_prob=softmax(X)
  4. X,X_prob,X_prob.sum(1)
  1. #实现softmax回归模型
  2. def net(X):
  3. return softmax(torch.matmul(X.reshape((-1,w.shape[0])),w)+b) #-1代表模型自己算一下维度,基本等于批量大小256,W.shape[0]等于784,也就是X为(256,784)的矩阵
  1. #交叉熵的损失
  2. #创建一个数据y_hat,其中包含2个样本在3个类别的预测概率,使用y作为y_hat中的概率的索引
  3. y=torch.tensor([0,2])
  4. y_hat=torch.tensor([[0.1,0.3,0.6],[0.3,0.2,0.5]])
  5. y_hat[[0,1],y] #这个操作的含义便是拿出y的一个值为0,这意味着在y_hat的第一列的第一位,拿出y的第二个值为2,然后对应的是y_hat的第三位
  1. #定义交叉熵损失函数
  2. def cross_entrppy(y_hat,y):
  3. return -torch.log(y_hat[range(len(y_hat)),y])
  4. cross_entrppy(y_hat,y)
  1. #将预测类别与真实y元素进行比较
  2. def accuracy(y_hat,y):
  3. """计算预测正确的数量"""
  4. if len(y_hat.shape)>1 and y_hat.shape[1]>1:#y_hat是大于2维的矩阵
  5. y_hat=y_hat.argmax(axis=1) #将每一行中最大的那个数存到y_hat之中,也就是预测分类的类别
  6. cmp=y_hat.type(y.dtype)==y #将y_hat转换为y的数据类型,这样才能进行比较
  7. return float(cmp.type(y.dtype).sum()) #将cmp转成y一样的形状然后求和
  8. accuracy(y_hat,y)/len(y)
  1. ##评估在任意模型net的准确率
  2. def evaluate_accuracy(net,data_iter):
  3. """计算在指定数据集上模型的精度"""
  4. if isinstance(net,torch.nn.Module):
  5. net.eval() #将模型设置为评估模式,不要计算梯度
  6. metric=Accumulator(2)
  7. for X,y in data_iter:#将数据放入迭代器之中
  8. metric.add(accuracy(net(X),y),y.numel())#第一列假如模型估计的样本数,第二列放入样本的总数
  9. return metric[0]/metric[1]#返回分类正确的样本数和总样本数的商,也就是计算准确率
  1. #实现Accumulator,在实例中创建了2个变量,用于分别存储在正确预测的数量和预测的总数量
  2. class Accumulator:
  3. """在n个变量上累加"""
  4. def __init__(self,n):
  5. self.data=[0,0]*n
  6. def add(self,*args):
  7. self.data=[a+float(b) for a ,b in zip(self.data,args)]
  8. def reset(self):
  9. self.data=[0.0]*len(self.data)
  10. def __getitem__(self,idx):
  11. return self.data[idx]
  12. evaluate_accuracy(net,test_iter) #结果和随机差不多
  1. def train_epoch_ch3(net,train_iter,loss,updater):
  2. if isinstance(net,torch.nn.Module):
  3. metric=Accumulator(3)
  4. for X,y in train_iter:
  5. y_hat=net(X)
  6. l=loss(y_hat,y)
  7. if isinstance(updater,torch.optim.Optimizer):
  8. updater.zero_grad()
  9. l.backward()
  10. updater.step()
  11. metric.add(
  12. float(l)*len(y),accuracy(y_hat,y),y.size().numel())
  13. else:
  14. l.sum().backward()
  15. updater(X.shape[0])
  16. metric.add(float(l.sum()),accuracy(y_hat,y),y.numel())
  17. return metric[0]/metric[2],metric[1]/metric[2]
  1. class Animator:
  2. """在动画中绘制数据。"""
  3. def __init__(self, xlabel=None, ylabel=None, legend=None, xlim=None,
  4. ylim=None, xscale='linear', yscale='linear',
  5. fmts=('-', 'm--', 'g-.', 'r:'), nrows=1, ncols=1,
  6. figsize=(3.5, 2.5)):
  7. # 增量地绘制多条线
  8. if legend is None:
  9. legend = []
  10. d2l.use_svg_display()
  11. self.fig, self.axes = d2l.plt.subplots(nrows, ncols, figsize=figsize)
  12. if nrows * ncols == 1:
  13. self.axes = [self.axes,]
  14. # 使用lambda函数捕获参数
  15. self.config_axes = lambda: d2l.set_axes(self.axes[
  16. 0], xlabel, ylabel, xlim, ylim, xscale, yscale, legend)
  17. self.X, self.Y, self.fmts = None, None, fmts
  18. def add(self, x, y):
  19. # 向图表中添加多个数据点
  20. if not hasattr(y, "__len__"):
  21. y = [y]
  22. n = len(y)
  23. if not hasattr(x, "__len__"):
  24. x = [x] * n
  25. if not self.X:
  26. self.X = [[] for _ in range(n)]
  27. if not self.Y:
  28. self.Y = [[] for _ in range(n)]
  29. for i, (a, b) in enumerate(zip(x, y)):
  30. if a is not None and b is not None:
  31. self.X[i].append(a)
  32. self.Y[i].append(b)
  33. self.axes[0].cla()
  34. for x, y, fmt in zip(self.X, self.Y, self.fmts):
  35. self.axes[0].plot(x, y, fmt)
  36. self.config_axes()
  37. display.display(self.fig)
  38. display.clear_output(wait=True)
  1. def train_ch3(net, train_iter, test_iter, loss, num_epochs, updater):
  2. """训练模型。"""
  3. animator = Animator(xlabel='epoch', xlim=[1, num_epochs], ylim=[0.3, 0.9],
  4. legend=['train loss', 'train acc', 'test acc'])
  5. for epoch in range(num_epochs):
  6. train_metrics = train_epoch_ch3(net, train_iter, loss, updater)
  7. test_acc = evaluate_accuracy(net, test_iter)
  8. animator.add(epoch + 1, train_metrics + (test_acc,))
  9. train_loss, train_acc = train_metrics
  1. #小批量随机梯度下降来优化模型的损失函数
  2. lr=0.1
  3. def updater(batch_size):
  4. return d2l.sgd([w,b],lr,batch_size)
  1. #训练模型10个迭代周期
  2. num_epochs=10
  3. train_ch3(net,train_iter,test_iter,cross_entrppy,num_epochs,updater)

image.png

  1. def predict_ch3(net,test_iter,n=6):
  2. """预测标签"""
  3. for X,y in test_iter:
  4. break
  5. trues=d2l.get_fashion_mnist_labels(y)
  6. preds=d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))
  7. titles=[true+'\n'+pred for true,pred in zip(trues,preds)]
  8. d2l.show_images(X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])
  9. predict_ch3(net, test_iter)

softmax回归的简洁实现

  1. #softmax回归的简洁实现
  2. #通过深度学习框架的高级API实现softmax回归
  3. import torch
  4. from torch import nn
  5. from d2l import torch as d2l
  6. batch_size=256
  7. train_iter,test_iter=d2l.load_data_fashion_mnist(batch_size)
  1. #softmax的回归的输出层是一个全连接层
  2. #pytorch不会隐式地调整输入的形状
  3. #因此,我们定义了展平层(flatten)在线性层前调整网络输入的形状
  4. #初始化
  5. net=nn.Sequential(nn.Flatten(),nn.Linear(784,10))#nn.Flatten()也就是将第0维度保留,剩下全部展开为向量,也就是类似于宽转长
  6. def init_weights(m):
  7. if type(m)==nn.Linear:
  8. nn.init.normal_(m.weight,std=0.01)#如果是线性回归,那么就将w全部改为方差为1
  9. net.apply(init_weights)
  1. #在交叉熵损失函数中传递未归一化的预测,并同时计算 softmax及其对数
  2. loss=nn.CrossEntropyLoss()
  1. num_epochs=10
  2. d2l.train_ch3(net,train_iter,test_iter,loss,num_epochs,trainer)
  1. def predict_ch3(net,test_iter,n=6):
  2. """预测标签"""
  3. for X,y in test_iter:
  4. break
  5. trues=d2l.get_fashion_mnist_labels(y)
  6. preds=d2l.get_fashion_mnist_labels(net(X).argmax(axis=1))
  7. titles=[true+'\n'+pred for true,pred in zip(trues,preds)]
  8. d2l.show_images(X[0:n].reshape((n, 28, 28)), 1, n, titles=titles[0:n])
  9. predict_ch3(net, test_iter)

image.png