pytorch(四、五、八)用pytorch实现线性回归和逻辑斯蒂回归(分类)以及多分类问题

线性回归

代码过程

训练过程:

  1. 准备数据集
  2. 设计模型(用来计算 y ^ \hat y y^
  3. 构造损失函数和优化器(API)
  4. 训练周期(前馈、反馈、更新)

准备数据

这里的输入输出数据均表示为3×1的,也就是维度均为1

# 行表示实例数量,列表示维度feature
import torch
x_data=torch.Tensor([[1.0],[2.0],[3.0]])
y_data=torch.Tensor([[2.0],[4.0],[6.0]])

设计模型

模型继承Module类,并且必须要实现 init 和 forward 两个方法,其中self.linear=torch.nn.Linear(1,1)表示实例化Linear类,这个类是可调用的,其__call__函数调用了 forward 方法

class LinearModel(torch.nn.Module):
    def __init__(self):
        super(LinearModel,self).__init__()
        # weight 和 bias 1 1 
        self.linear=torch.nn.Linear(1,1)
        
    def forward(self,x):
        # callable
        y_pred=self.linear(x)
        return y_pred

# callable
model=LinearModel()

pytorch中的linear类是在某一个数据上应用线性转换,其公式表达为 y = x w T + b y=xw^T+b y=xwT+b

class torch.nn.Linear(in_features,out_features,bias=True) :其中in_features和out_features分别表示输入和输出的数据的维度(列的数量),bias表示偏置,默认是true,该类有两个参数

  • weight:可学习参数,值从均匀分布 U ( − k , k ) U(-\sqrt k,\sqrt k) U(k ,k )中获取,其中 k = 1 i n _ f e a t u r e s k=\frac{1}{in\_features} k=in_features1
  • bias:shape和输出的维度一样,也是从分布 U ( − k , k ) U(-\sqrt k,\sqrt k) U(k ,k )中初始化的
    在这里插入图片描述

设计构造函数与优化器

# 构造损失函数和优化器
criterion=torch.nn.MSELoss(size_average=False)

# w和b--->parameters
opyimizer=torch.optim.SGD(model.parameters(),lr=0.01)

在这里插入图片描述
在这里插入图片描述

训练过程

# 训练过程
for epoch in range(100):
    y_pred=model(x_data)
    loss=criterion(y_pred,y_data)
    # loss标量,自动调用__str__()
    print(epoch,loss)
    
    optimizer.zero_grad()
    # backward
    loss.backward()
    # update
    optimizer.step()

训练代码和结果

# 行表示实例数量,列表示维度feature
import torch
x_data=torch.Tensor([[1.0],[2.0],[3.0]])
y_data=torch.Tensor([[2.0],[4.0],[6.0]])

class LinearModel(torch.nn.Module):
    def __init__(self):
        super(LinearModel,self).__init__()
        # weight 和 bias 1 1 
        self.linear=torch.nn.Linear(1,1)
        
    def forward(self,x):
        # callable
        y_pred=self.linear(x)
        return y_pred

# callable
model=LinearModel()

# 构造损失函数和优化器
criterion=torch.nn.MSELoss(size_average=False)
optimizer=torch.optim.SGD(model.parameters(),lr=0.01)

# 训练过程
for epoch in range(100):
    y_pred=model(x_data)
    loss=criterion(y_pred,y_data)
    # loss标量,自动调用__str__()
    print(epoch,loss)
    
    optimizer.zero_grad()
    # backward
    loss.backward()
    # update
    optimizer.step()
    
    
# 打印信息
print('w=',model.linear.weight.item())
print('b=',model.linear.bias.item())

x_test=torch.Tensor([4.0])
y_test=model(x_test)
print('y_pred=',y_test.data)

在这里插入图片描述


pytorch中的Linear层的底层原理(个人喜欢,不用看)

我们在课本使用到的线性函数的基本公式表达为 y = x w T + b y=xw^T+b y=xwT+b,但是在Linear层中,当输入特征被Linear层接收是,它会接收后转置,然后乘以权重矩阵,得到的是输出特征的转置,换句话说可以在底层使用Linear,它实际上做的是 y T = w x T + b y^T=wx^T+b yT=wxT+b。可以使用下面的案例进行验证:

在这里插入图片描述

普通矩阵乘法实现

很明显,上面的图标表示一个 3×4 的矩阵乘以 4×1 的矩阵,得到一个 3×1 的输出矩阵,使用普通矩阵的乘法实现如下。

import torch

in_features=torch.tensor([1,2,3,4],dtype=torch.float32)
weight_matrix=torch.tensor([
    [1,2,3,4],
    [2,3,4,5],
    [3,4,5,6]
],dtype=torch.float32)

weight_matrix.matmul(in_features)# 矩阵乘法

实现截图:
在这里插入图片描述

Linear层实现

# 这里还是使用上面使用过的数据
import torch
in_features=torch.tensor([1,2,3,4],dtype=torch.float32)
weight_matrix=torch.tensor([
    [1,2,3,4],
    [2,3,4,5],
    [3,4,5,6]
],dtype=torch.float32)

print(weight_matrix.matmul(in_features))# 矩阵乘法

fc = torch.nn.Linear(in_features=4, out_features=3, bias=False)
# 这里是随机一个权重矩阵
print('fc.weight',fc.weight)
fc(in_features)

输出结果:
在这里插入图片描述

print('fc.weight',fc.weight)

# 使用上面的权重矩阵进行计算
fc.weight = torch.nn.Parameter(weight_matrix)
print('fc.weight',fc.weight)
fc(in_features)

结果截图:
在这里插入图片描述

可以看到上面截图与下面的截图的区别,一开始随机一个权重的时候,进行运算,使用到前面提及到的权重矩阵后,Linear层进行运算之后,得到与使用普通矩阵乘法一样的结果,相同的结果说明,Linear底层的实现与上面的矩阵乘法的逻辑是一致的

以上的论证可以说明,Linear的底层实现其实是 y T = w x T + b y^T=wx^T+b yT=wxT+b,而不是 y = x w T + b y=xw^T+b y=xwT+b,可能会有人好奇,为什么书本上都是写的后者而不是写前者,其实本质上二者都一样,前者的转置就是后者。

回调机制

pytorch学习(一)线性模型中,第一个代码中,我们没有通过pytorch实现线性模型的时候,我们会显式调用forward函数,计算前馈的值,我们是这样写的y_pred_val=forward(x_val),但是在使用pytorch之后,我们是这样写的y_pred=model(x_data),直接实例化一个对象,然后通过对象直接计算预测值(前馈值),但是并没有使用到forward函数。这是因为pytorch模块类中实现了python中一个特殊的函数,也就是回调函数

如果一个类实现了回调方法,那么只要对象实例被调用,这个特殊的方法也会被调用。我们不直接调用forward()方法,而是调用对象实例。在对象实例被调用之后,在底层调用了__ call __方法,然后调用了forward()方法。这适用于所有的PyTorch神经网络模块。

以上仅代表小白个人学习观点,如有错误欢迎批评指正。

参考



逻辑斯蒂回归

逻辑斯蒂回归解决的事分类问题,分类输出的是类别的概率

模型

在线性模型中,通过 y = w x + b y=wx+b y=wx+b输出的是一个实数值,但是在分类问题中,输出的是类别的概率,所以需要一个函数,把实数值映射到[0,1]之间,表示概率,这个函数为 sigmoid 函数 y = 1 1 + e − x ∈ [ 0 , 1 ] y=\frac{1}{1+e^{-x}}∈[0,1] y=1+ex1[0,1],sigmoid函数属于饱和函数。

以上,逻辑斯蒂回归模型的公式为: y ^ = σ ( x ∗ w + b ) \hat{y}=\sigma(x*w+b) y^=σ(xw+b)

在这里插入图片描述

损失函数

在线性回归中,计算损失一般是使用均方误差(预测值与真实值的差值的平方和的累加),在回归问题中,均方误差表示数轴上两个值之间的距离,但是分类问题中,输出的结果表示的是概率(分布),使用距离是没有意义的,所以分类问题中的损失函数并不是均方误差。

在逻辑斯蒂回归中,使用的是BCE
在这里插入图片描述

代码和结果

import torch
import torch.nn.functional as F
import numpy as np
import matplotlib.pyplot as plt


x_data=torch.Tensor([[1.0],[2.0],[3.0]])
y_data=torch.Tensor([[0],[0],[1]])

class LogisticRegressionModel(torch.nn.Module):
    def __init__(self):
        super(LogisticRegressionModel,self).__init__()
        self.linear=torch.nn.Linear(1,1)
        
    def forward(self,x):
        y_pred=F.sigmoid(self.linear(x))
        return y_pred
    
model=LogisticRegressionModel()

# 损失函数
criterion=torch.nn.BCELoss(size_average=False)
# 优化器
optimizer=torch.optim.SGD(model.parameters(),lr=0.01)

# 训练
for epoch in range(1000):
    y_pred=model(x_data)
    loss=criterion(y_pred,y_data)
    print(epoch,loss.item())
    
    optimizer.zero_grad()
    loss.backward()
    optimizer.step()
    
# linspace与range函数类似,用于生成均匀分布的数值序列
# np.linspace(start=0,stop=10,num=200)
x=np.linspace(0,10,200)
# 数据集,test,生成200*1的矩阵
x_t=torch.Tensor(x).view((200,1))
y_t=model(x_t)
y=y_t.data.numpy()
plt.plot(x,y)
plt.plot([0,10],[0.5,0.5],c='r')
plt.xlabel("Hours")
plt.ylabel("Probability of Pass")
# 显示网格线
plt.grid()
plt.show()

在这里插入图片描述

多分类问题

mnist数据集训练

import torch
from torchvision import transforms
from torchvision import datasets
from torch.utils.data import DataLoader
import torch.nn.functional as F
import torch.optim as optim

batch_size=64
# 对图像进行处理,对原来图像从{0,255}-归一化->{0,1},把图像的维度变成c*w*h的 
transform=transforms.Compose([transforms.ToTensor(),transforms.Normalize((0.1307,),(0.3081,))])

train_dataset=datasets.MNIST(root='./dataset/mnist/',train=True,download=True,transform=transform)
train_loader=DataLoader(train_dataset,shuffle=True,batch_size=batch_size)

test_dataset=datasets.MNIST(root='./dataset/mnist/',train=False,download=True,transform=transform)
test_loader=DataLoader(test_dataset,shuffle=False,batch_size=batch_size)

class Model(torch.nn.Module):
    def __init__(self):
        super(Model,self).__init__()
        self.l1=torch.nn.Linear(784,512)
        self.l2=torch.nn.Linear(512,256)
        self.l3=torch.nn.Linear(256,128)
        self.l4=torch.nn.Linear(128,64)
        self.l5=torch.nn.Linear(64,10)
    
    def forward(self,x):
        x=x.view(-1,784)
        x=F.relu(self.l1(x))
        x=F.relu(self.l2(x))
        x=F.relu(self.l3(x))
        x=F.relu(self.l4(x))
        # 最后一层不用激活函数
        return self.l5(x)
        
model=Model()
criterion=torch.nn.CrossEntropyLoss()
# 带冲量为0.5
optimizer=optim.SGD(model.parameters(),lr=0.01,momentum=0.5)

def train(epoch):
    running_loss=0.0
    for batch_idx,data in enumerate(train_loader,0):
        inputs,targets=data
        # forward
        y_pred=model(inputs)
        loss=criterion(y_pred,targets)
        # backward
        optimizer.zero_grad()
        loss.backward()
        # update
        optimizer.step()
        
        running_loss+=loss.item()
        if batch_idx % 300 ==299:
            print('[%d,%5d] loss:%.3f'%(epoch+1,batch_idx+1,running_loss/300))
            running_loss=0.0

def test():
    correct=0
    total=0
    # 不需要计算梯度
    with torch.no_grad():
        for data in test_loader:
            images,labels=data
            outputs=model(images)
            # 取最大值的下标 dim表示维度
            _,predicted=torch.max(outputs.data,dim=1)
            total+=labels.size(0)
            correct+=(predicted==labels).sum().item()
    print('Accuracy on test set:%d %%'%(100*correct/total))
    
if __name__=='__main__':
    for epoch in range(10):
        train(epoch)
        test()

在这里插入图片描述

  • 21
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值