前提:不是前馈神经网络,没有隐藏层。
一:Logistic回归实验–人工构造数据集,手动构造模型
要求:
动手从0实现logistic回归,实现二分类,人工构造数据集,并分析loss、训练集、测试集的准确率。(要求从零实现二元交叉熵)
实验过程:
1.1人工构造数据集
1. # #自定义数据---训练集 2. num_inputs = 2 3. n_data = torch.ones(1500, num_inputs) # 数据的基本形态 4. x1 = torch.normal(2 * n_data, 1) # shape=(1500, 2) #每个元素是从 均值=2*n_data中对应位置的取值,标准差为1的正态分布中随机生成的 5. y1 = torch.ones(1500) # 类型0 shape=(1500, 1) 6. x2 = torch.normal(-2 * n_data, 1) # 类型1 shape=(1500, 2) 7. y2 = torch.zeros(1500) # 类型1 shape=(1500, 1) 8. # 注意 x, y 数据的数据形式一定要像下面一样 (torch.cat 是合并数据)---按行合并 9. trainfeatures = torch.cat((x1, x2), 0).type(torch.FloatTensor) 10. trainlabels = torch.cat((y1, y2), 0).type(torch.FloatTensor) 11. # #自定义数据---测试集 12. n_data = torch.ones(700, num_inputs) # 数据的基本形态 13. x1 = torch.normal(2 * n_data, 1) # shape=(700, 2) #每个元素是从 均值=2*n_data中对应位置的取值,标准差为1的正态分布中随机生成的 14. y1 = torch.ones(700) # 类型0 shape=(700, 1) 15. x2 = torch.normal(-2 * n_data, 1) # 类型1 shape=(700, 2) 16. y2 = torch.zeros(700) # 类型1 shape=(700, 1) 17. # 注意 x, y 数据的数据形式一定要像下面一样 (torch.cat 是合并数据)---按行合并 18. testfeatures = torch.cat((x1, x2), 0).type(torch.FloatTensor) 19. testlabels = torch.cat((y1, y2), 0).type(torch.FloatTensor)
注:这里我随机生成了3000个数据集作为训练集,其中前1500作为正样本标签为1,而其值为正数,后1500作为负样本,标签0,其值是负数。并且对于正样本和负样本分别是服从高斯分布的(均值为2,标准差为1);然后随机生成了1400个数据集作为测试集,同理前700个作为正样本标签为1,而其值为正数,后700作为负样本,标签是0,其值是负数,同理属于相同的高斯分布,其比例为15/7.
1.2读取数据集
1. #读取数据 2. def data_iter(batch_size, features, labels): 3. num_examples = len(features) 4. indices = list(range(num_examples)) 5. random.shuffle(indices) # 样本的读取顺序是随机的 6. for i in range(0, num_examples, batch_size): 7. j = torch.LongTensor(indices[i: min(i + batch_size, num_examples)]) # 最后一次可能不足一个batch 8. yield features.index_select(0, j), labels.index_select(0, j) 9. 10. #初始化W:[2,1]和b[1] 11. w = torch.tensor(np.random.normal(0, 0.01, (num_inputs, 1)), dtype=torch.float32) 12. b = torch.zeros(1, dtype=torch.float32) 13. b.requires_grad_(requires_grad=True) 14. w.requires_grad_(requires_grad=True)
1.3手动定义模型、损失函数、优化函数
1. #逻辑回归=线性网络+sigmoid激活函数 2. def logits(X, w, b): 3. y = torch.mm(X, w) + b 4. return 1/(1+torch.pow(np.e,-y)) 5. 6. #手动实现二元交叉熵损失函数 7. def logits_loss(y_hat, y): 8. y = y.view(y_hat.size()) 9. return -y.mul(torch.log(y_hat))-(1-y).mul(torch.log(1-y_hat)) 10. 11. #优化函数 12. def sgd(params, lr, batch_size): 13. for param in params: 14. param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data
注:Logistic回归的模型定义,以及二元交叉熵的损失函数和优化函数。需要注意的是矩阵相乘利用的是torch.mm,而矩阵对应位置相乘是利用的.mul,另外利用view()函数统一为同型张量。
1.4定义测试集准确率函数
1. #测试集准确率 2. def evaluate_accuracy(): 3. acc_sum,n = 0.0,0 4. for X,y in data_iter(batch_size, testfeatures, testlabels): 5. #print(len(X)) 小批量数据集 每个X中有 256个图像 6. #print((net(X).argmax(dim=1)==y).float().sum().item()) 7. y_hat = net(X, w, b) 8. y_hat = torch.squeeze(torch.where(y_hat>0.5,torch.tensor(1.0),torch.tensor(0.0))) 9. acc_sum += (y_hat==y).float().sum().item() 10. n+=y.shape[0] 11. return acc_sum/n
注:这里在自定义的测试集数据集上,进行迭代,其主要是将预测值>0.5的部分归为1类,<0.5的部分归为0类,然后和真实值对比统计相同的数量,累加比值,计算一个epoch下的比例作为一次epoch的准确率。
1.5开始训练并计算loss、训练集准确率和测试集准确率
1. #开始训练 2. lr = 0.0005 3. num_epochs = 300 4. net = logits 5. loss = logits_loss 6. batch_size = 50 7. epochlist = np.arange(1,num_epochs+1) 8. losslist = [] 9. for epoch in range(num_epochs): # 训练模型一共需要num_epochs个迭代周期 10. train_l_num, train_acc_num,n = 0.0,0.0,0 11. # 在每一个迭代周期中,会使用训练数据集中所有样本一次 12. for X, y in data_iter(batch_size, trainfeatures, trainlabels): # x和y分别是小批量样本的特征和标签 13. y_hat = net(X, w, b) 14. l = loss(y_hat, y).sum() # l是有关小批量X和y的损失 15. l.backward() # 小批量的损失对模型参数求梯度 16. sgd([w, b], lr, batch_size) # 使用小批量随机梯度下降迭代模型参数 17. w.grad.data.zero_() # 梯度清零 18. b.grad.data.zero_() # 梯度清零 19. #计算每个epoch的loss 20. train_l_num += l.item() 21. #计算训练样本的准确率 22. y_hat = torch.squeeze(torch.where(y_hat>0.5,torch.tensor(1.0),torch.tensor(0.0))) 23. train_acc_num += (y_hat==y).sum().item() 24. #每一个epoch的所有样本数 25. n+= y.shape[0] 26. #train_l = loss(net(trainfeatures, w, b), trainlabels) 27. #计算测试样本的准确率 28. test_acc = evaluate_accuracy() 29. print('epoch %d, loss %.4f,train_acc %f,test_acc %f'%(epoch+1,train_l_num/n, train_acc_num/n, test_acc))
注:这里定义学习率为0.0005,迭代次数为300,以及batch_size为50,并,但是这里需要注意的是学习率的设置,起初在设置0.01以及0.001的时候,在loss逐渐降低的时候,会出现NAN的现象,推测是梯度消失了,所以学习率不能过大,从而设置了0.0005,然后batch_size不能过大,否则迭代效果不是太好,而迭代次数不能太大,否则也会出现Nan现象。在计算训练集准确率的时候同理将预测值>0.5的部分归为1类,<0.5的部分归为0类,然后和真实值对比统计相同的数量,但是不同于测试集的是其是利用的是在这次epoch的w和b进行预测,而测试集是利用的是这次epoch后更新的w和b参数进行的预测。
二:Logistic回归实验–人工构造数据集,利用pytorch定义模型
要求:
利用pytorch实现logistic回归,实现二分类,人工构造数据集,并分析loss、训练集、测试集的准确率。(要求从零实现二元交叉熵)
2.1人工构造数据
同1.1
2.2利用torch. utils.data读取数据集
1. #读取数据 2. batch_size = 50 3. # 将训练数据的特征和标签组合 4. dataset = Data.TensorDataset(trainfeatures, trainlabels) 5. # 把 dataset 放入 DataLoader 6. data_iter = Data.DataLoader( 7. dataset=dataset, # torch TensorDataset format 8. batch_size=batch_size, # mini batch size 9. shuffle=True, # 是否打乱数据 (训练集一般需要进行打乱) 10. num_workers=0, # 多线程来读数据, 注意在Windows下需要设置为0 11. ) 12. # 将测试数据的特征和标签组合 13. dataset = Data.TensorDataset(testfeatures, testlabels) 14. # 把 dataset 放入 DataLoader 15. test_data_iter = Data.DataLoader( 16. dataset=dataset, # torch TensorDataset format 17. batch_size=batch_size, # mini batch size 18. shuffle=True, # 是否打乱数据 (训练集一般需要进行打乱) 19. num_workers=0, # 多线程来读数据, 注意在Windows下需要设置为0 20. )
注:这一部分借鉴的课件,利用PyTorch提供的 data 库来读取数据。由于data常用作变量名,这里将导入的 data模块用 data代替。对前面的读取数据部分可以使用 data库来处理。然后在每一次迭代中,使用 data随机读取包含50个数据样本的小批量,其中分别包括训练集和测试集的迭代组合。
2.3利用torch.nn定义模型、损失函数、优化函数
1. #nn.Module 定义模型 2. class LogisticRegression(nn.Module): 3. def __init__(self,n_features): 4. super(LogisticRegression, self).__init__() 5. self.lr = nn.Linear(n_features, 1) 6. self.sm = nn.Sigmoid() 7. 8. def forward(self, x): 9. x = self.lr(x) 10. x = self.sm(x) 11. return x 12. #初始化模型 13. logistic_model = LogisticRegression(num_inputs) 14. #定义损失函数 15. criterion = nn.BCELoss() 16. #定义优化器 17. optimizer = torch.optim.SGD(logistic_model.parameters(), lr=1e-3)
注:利用torch.nn相关模块进行Logistic回归的模型定义,以及二元交叉熵的损失函数和优化函数。这里需要注意的是学习率的选取,不能太大,另外利用了nn.BCELoss()损失函数,这个是不包含sigmoid激活函数的,需要在模型定义上自行定义。
2.4利用torch.nn中的init模块进行参数随机初始化
1. #参数初始化 2. init.normal_(logistic_model.lr.weight, mean=0, std=0.01) 3. init.constant_(logistic_model.lr.bias, val=0) #也可以直接修改bias的data: net[0].bias.data.fill_(0) 4. print(logistic_model.lr.weight) 5. print(logistic_model.lr.bias)
注:这一部分需要额外注意,其中的初始化方式在课件中,是net[0].weight的定义方式,而使用时是不允许这样直接利用索引访问的,可以自行打印一下模型的结构,然后选中lr线性部分,初始化其权重w(高斯分布)和偏置b(0即可)。
2.5定义测试集准确率函数
1. def evaluate_accuracy(): 2. acc_sum,n = 0.0,0 3. for X,y in test_data_iter: 4. #print(len(X)) 小批量数据集 每个X中有 256个图像 5. #print((net(X).argmax(dim=1)==y).float().sum().item()) 6. y_hat = logistic_model(X) 7. y_hat = torch.squeeze(torch.where(y_hat>0.5,torch.tensor(1.0),torch.tensor(0.0))) 8. acc_sum += (y_hat==y).float().sum().item() 9. n+=y.shape[0] 10. return acc_sum/n
2.6开始训练并计算loss、训练集准确率和测试集准确率
1. #开始训练 2. num_epochs = 300 3. # epochlist = np.arange(1,num_epochs+1) 4. # losslist = [] 5. for epoch in range( num_epochs): 6. train_l_num, train_acc_num,n = 0.0,0.0,0 7. for X, y in data_iter: 8. y_hat = logistic_model(X) 9. l = criterion(y_hat, y.view(-1, 1)) 10. optimizer.zero_grad() # 梯度清零,等价于logistic_model.zero_grad() 11. l.backward() 12. # update model parameters 13. optimizer.step() 14. #计算每个epoch的loss 15. train_l_num += l.item() 16. #计算训练样本的准确率 17. y_hat = torch.squeeze(torch.where(y_hat>0.5,torch.tensor(1.0),torch.tensor(0.0))) 18. train_acc_num += (y_hat==y).sum().item() 19. #每一个epoch的所有样本数 20. n+= y.shape[0] 21. #计算测试样本的准确率 22. test_acc = evaluate_accuracy() 23. print('epoch %d, loss %.4f,train_acc %f,test_acc %f'%(epoch+1,train_l_num/n, train_acc_num/n, test_acc))
注:不同于自定义的模型等训练过程,这里的梯度清零以及梯度更新都是在优化器的函数中进行,其他的loss、训练集准确率和测试集准确率都是和上面1.5的原理一样,这里不再赘述。
三:Softmax回归实验–数据集为Fashion-MNIST,手动构造模型
数据集下载:https://download.csdn.net/download/qq_37534947/12675880
要求:动手从0实现softmax回归,利用Fashion-MNIST数据集,并分析loss、训练集、测试集的准确率。
3.1导入包
1. import torch 2. import numpy as np 3. import random 4. from IPython import display 5. from matplotlib import pyplot as plt 6. import torchvision 7. import torchvision.transforms as transforms
3.2下载Fashion-MINIST数据并读取
1. #下载Fashion-MINIST数据 2. mnist_train = torchvision.datasets.FashionMNIST(root='./Datasets/FashionMNIST', train=True, 3. download=True, transform=transforms.ToTensor()) 4. mnist_test = torchvision.datasets.FashionMNIST(root='./Datasets/FashionMNIST', train=False, 5. download=True, transform=transforms.ToTensor()) 6. #读取数据 7. batch_size = 256 8. train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, 9. num_workers=0) 10. test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, 11. num_workers=0) 12. print(test_iter)
注:这里因为数据集过大,下载超级慢,所以从别的博客下载的,然后放到指定位置即可,数据集主要包含十类物体,并且每类为6000张图片。然后利用DataLoader加载读取数据,其中测试集一共10000张。
3.3初始化参数W,b
1. # 输入与输出 2. num_inputs = 784 3. num_outputs = 10 4. 5. W = torch.tensor(np.random.normal(0,0.01,(num_inputs,num_outputs)),dtype=torch.float) 6. b = torch.zeros(num_outputs,dtype = torch.float) 7. #开启梯度track 8. W.requires_grad_(requires_grad = True) 9. b.requires_grad_(requires_grad = True)
注:这里需要注意的是输入和输出,一张图片的像素为28*28=784,所以输入是784,而种类一共为10类,所以输出是10。然后W和b的初始化和之前一样。
3.4手动定义激活函数和模型
1. #定义softmax函数 2. def softmax(X): 3. X_exp = X.exp() 4. partition = X_exp.sum(dim = 1, keepdim=True) 5. return X_exp / partition 6. 7. #手动实现定义模型 8. def net(X): 9. #torch.mm 矩阵相乘 view()改变矩阵维度为1行 num_input列 10. f_x = torch.mm(X.view((-1,num_inputs)),W) + b 11. return softmax(f_x)
注:这里利用矩阵相乘y = WX+b,然后再利用softmax激活函数将其输出的和统一为1.
3.5手动实现交叉熵损失函数
1. #手动实现交叉熵损失函数 2. def cross_entropy(y_hat, y): 3. #1按行取 4. return -torch.log(y_hat.gather(1, y.view(-1,1)))
注:这里主要是gather(dim,tensor)的方法的使用,dim=1,表示按行取,即从每个y_hat的10个概率结果上,取真值相对应的位置,这里的y每个值代表真值的类别,取值是,即只计算与真值相对应的y_hat,可以将真值理解为one_hot编码,只有真值对应在这里插入代码片的位置保留,其他的为0则略掉,所以意义一样。
3.6手动定义sgd优化函数
1. #优化函数 2. def sgd(params, lr, batch_size): 3. for param in params: 4. param.data -= lr * param.grad / batch_size # 注意这里更改param时用的param.data
3.7定义测试集准确率函数
1. def evaluate_accuracy(data_iter,net): 2. acc_sum,n = 0.0,0 3. for X,y in data_iter: 4. #print(len(X)) 小批量数据集 每个X中有 256个图像 5. #print((net(X).argmax(dim=1)==y).float().sum().item()) 6. acc_sum += (net(X).argmax(dim=1)==y).float().sum().item() 7. n+=y.shape[0] 8. return acc_sum/n
注:这里定义的是测试集的准确率的计算,在后面训练的每个epoch中进行计算,其原理是在每个epoch训练完后,更新参数w和b,然后在最新的w和b的基础上,对所有的测试集输入到net网络中,进行测试,其中根据判断每一个输出是否和真值的位置相对应,则认为检测准确,反之检测失误,然后统计最后的正确数量和测试集数量做比。
3.8定义训练函数,内置loss、训练集准确率
1. def train(net, train_iter,test_iter,loss,num_epochs,batch_size,params=None,lr=None,optimizer = None): 2. for epoch in range(num_epochs): 3. #模型训练次数 num_epochs次 4. train_l_num, train_acc_num,n = 0.0,0.0,0 5. for X,y in train_iter: 6. #X 为小批量256个图像 1*28*28 y为标签 7. # 计算X softmax下的值 与损失函数值 8. y_hat = net(X) 9. l = loss(y_hat,y).sum() 10. l.backward() 11. sgd([W, b], lr, batch_size) # 使用小批量随机梯度下降迭代模型参数 12. W.grad.data.zero_() # 梯度清零 13. b.grad.data.zero_() 14. #计算每个epoch的loss 15. train_l_num += l.item() 16. #计算训练样本的准确率 17. train_acc_num += (y_hat.argmax(dim=1)==y).sum().item() 18. #每一个epoch的所有样本数 19. n+= y.shape[0] 20. #计算测试样本的准确率 21. test_acc = evaluate_accuracy(test_iter,net) 22. print('epoch %d, loss %.4f,train_acc %.3f,test_acc %.3f'%(epoch+1,train_l_num/n, train_acc_num/n, test_acc))
注:这里的流程和之前的一样,在训练集上取batch_size大小的训练集,然后输入到网络中,然后计算损失函数,反向传播求梯度,累加所有的loss,以及在batch_size下计算训练样本与真值的位置对应,统计检测准确的数量,然后在每个epoch结束后,进行所有loss和accuracy的统计与训练集数量做比,即可以求出loss和训练集的准确率,测试集准确率直接调用evaluate_accuray函数,需要注意的是,求每个epoch的测试集准确率,利用的是每次训练后更新的参数w和b,而训练集准确率和loss是统计训练时的结果,即不是更新后的w和b。
3.9训练模型
1. num_epochs ,lr = 30,0.1 2. train(net, train_iter, test_iter, cross_entropy, num_epochs,batch_size, [W, b], lr)
注:传入参数,训练网络,定义参数训练次数以及学习率,刚开始误差太多,所以学习率相比之前是比较大的。
四:Softmax回归实验–数据集为Fashion-MNIST,pytorch构造模型
数据集下载见:https://download.csdn.net/download/qq_37534947/12675880
要求:使用pytorch实现softmax回归,利用Fashion-MNIST数据集,并分析loss、训练集、测试集的准确率。
4.1导入包
1. import torch 2. import numpy as np 3. import random 4. from torch import nn 5. from IPython import display 6. from matplotlib import pyplot as plt 7. import torchvision 8. import torchvision.transforms as transforms 9. from torch.nn import init
4.2下载Fashion-MINIST数据并读取
1. #下载Fashion-MINIST数据 2. mnist_train = torchvision.datasets.FashionMNIST(root='./Datasets/FashionMNIST', train=True, 3. download=True, transform=transforms.ToTensor()) 4. mnist_test = torchvision.datasets.FashionMNIST(root='./Datasets/FashionMNIST', train=False, 5. download=True, transform=transforms.ToTensor()) 6. #读取数据 7. batch_size = 256 8. train_iter = torch.utils.data.DataLoader(mnist_train, batch_size=batch_size, shuffle=True, 9. num_workers=0) 10. test_iter = torch.utils.data.DataLoader(mnist_test, batch_size=batch_size, shuffle=False, 11. num_workers=0) 12. print(test_iter)
4.3确定输入和输出,利用nn模块定义网络模型
1. #定义输入和输出 2. num_inputs = 784 3. num_outputs = 10 4. #定义网络模型 5. class LinearNet(nn.Module): 6. def __init__(self, num_inputs, num_outputs): 7. super(LinearNet, self).__init__() 8. self.linear = nn.Linear(num_inputs, num_outputs) 9. def forward(self, x): # x shape: (batch, 1, 28, 28) 10. y = self.linear(x.view(x.shape[0], -1)) 11. return y 12. 13. net = LinearNet(num_inputs, num_outputs)
注:这里和之前的logistic实现几乎一样,主要是对于该模型的输出从1变成10.
4.4初始化相关参数
1. # 初始化参数w和b 2. init.normal_(net.linear.weight, mean=0, std=0.01) 3. init.constant_(net.linear.bias, val=0) 和b
4.5定义损失函数
1. #nn模块实现交叉熵损失函数--包含了softmax函数 2. cross_entropy = nn.CrossEntropyLoss()
注:这里不同于之前的损失函数和网络模型,而是通过CrossEntropyLoss将softmax函数和交叉熵损失函数合并了在一起,相比于之前单独实现,其有着更好的稳定性。
4.6定义优化函数-小批量梯度下降
1. #优化函数SGD 2. optimizer = torch.optim.SGD(net.parameters(), lr=0.1)
4.7定义测试集准确率函数
1. #测试集准确率 2. def evaluate_accuracy(data_iter,net): 3. acc_sum,n = 0.0,0 4. for X,y in data_iter: 5. #print(len(X)) 小批量数据集 每个X中有 256个图像 6. #print((net(X).argmax(dim=1)==y).float().sum().item()) 7. acc_sum += (net(X).argmax(dim=1)==y).float().sum().item() 8. n+=y.shape[0] 9. return acc_sum/n
4.8定义训练函数,内置loss、训练集准确率
1. def train(net, train_iter, test_iter, loss, num_epochs, batch_size,params=None, lr=None, optimizer=None): 2. for epoch in range(num_epochs): 3. train_l_sum, train_acc_sum, n = 0.0, 0.0, 0 4. for X, y in train_iter: 5. y_hat = net(X) 6. l = loss(y_hat, y).sum() 7. optimizer.zero_grad() # 梯度清零 8. l.backward() # 计算梯度 9. optimizer.step() # 随机梯度下降算法, 更新参数 10. train_l_sum += l.item() 11. #训练集准确率 12. train_acc_sum += (y_hat.argmax(dim=1) == y).sum().item() 13. n += y.shape[0] 14. test_acc = evaluate_accuracy(test_iter, net) 15. print('epoch %d, loss %.4f, train acc %.3f, test acc %.3f'% (epoch + 1, train_l_sum / n, train_acc_sum / n, test_acc))
注:这里的loss以及训练集准确率和上面1.8实现是一样的,就不加赘述,主要改变的是利用了nn模块自带的优化器optimizer来实现参数的更新;然后不同的是其将softmax激活函数和损失函数合并在了一起,所以在计算训练集准确率以及测试集准确率的时候其实是没有经过softmax层,但是这样不会影响,因为我们主要找的是10个结果中最大的,经不经过softmax层,其最大位置是不会变的。
4.9训练模型
1. num_epochs = 30 2. train(net, train_iter, test_iter, cross_entropy, num_epochs,batch_size,None,None, optimizer)