零基础一步一步pytorch实现Logistic regression逻辑回归编程教程

因为神经网络的一个神经元就是Logistic regression逻辑回归。

Logistic regression(LR)逻辑回归是什么?

它就是套一个概率分布模型上去。什么是模型?模型就是一个函数比如:f(x)=ax+b。至于逻辑回归套的那个概率分布模型是什么稍后再说,先说套概率分布模型上去有什么用?假如需要判断样本x是属于A类还是属于B类。在前面提到了模型就是一个函数,概率分布模型就是输入一个样本数据x,函数输出它属于A类的概率。逻辑回归只能分出两种类。x属于A类的概率大于0.5那就我们可以认为当前样本属于A类,如果概率小于0.5那就认为它属于B类。

那么逻辑回归套的是什么函数呢?答:f(x)=11+ewTxf(x) = \frac{1}{1+e^{-w^Tx}}f(x)=1+e−wTx1​。其中www是待求解的参数,不同的数据是不一样的。那我们怎么根据当前已有的数据调整得到最优的参数www?答:用梯度下降。

如果你对梯度下降不理解可以参考这两篇文章:

易懂的神经网络理论到实践(1):单个神经元+随机梯度下降学习逻辑与规则

{高中生能看懂的}梯度下降是个啥?

pytorch编程实践

本文的实践会先介绍使用的数据是什么。然后介绍如何检验自己程序是否正确。然后介绍如何用pytorch的反向传播求导(求导结果是否正确可以我们自己手动计算下来检验),并用这个导数实现梯度下降。然后介绍如何我们自己求导实现梯度下降(我们自己写的导数是否计算正确检验方法1是可以用前面反向传播求导结果检验2是我们自己手动计算看程序运行结果是否正确)。

使用pytorch自带的自动求导工具求导,然后实现梯度下降

大家一定要注意机器学习必备下面那几个步骤。写程序一定要对这些步骤烂熟于心。

  1. 加载数据
  2. 输入样本计算模型的输出
  3. 计算损失函数对参数w的导数(梯度)
  4. 梯度下降调节参数w
  5. 重复2-4

1.加载数据

首先我们需要加载数据,这次我们想实现的是让逻辑回归识别逻辑异或(两个数字相同那就是0,两个数字不同那就是1)。

所以数据(前面两个数字)与标签(最后那个数字)是:

1 1 0
1 0 1
0 1 0
0 0 1

现在我们用代码实现加载数据

# -*- coding:utf-8 -*-
import torch
import numpy as np
'''
用逻辑回归实现异或功能
'''
# 加载数据
data = torch.tensor([[1,1],[1,0],[0,1],[0,0]],dtype=torch.float)
label = torch.tensor([[0,1,1,0]],dtype=torch.float).t()

计算模型的输出

前面提到了逻辑回归的模型是f(x)=11+ewTxf(x) = \frac{1}{1+e^{-w^Tx}}f(x)=1+e−wTx1​。我们首先需要先初始化参数w。然后输入一个样本x,然后根据公式f(x)=11+ewTxf(x) = \frac{1}{1+e^{-w^Tx}}f(x)=1+e−wTx1​计算模型输出。接下来我们用代码实现它。很多同学不知道自己代码写的对不对,大家只需要自己算一下结果,然后看看程序输出是否符合自己计算结果

# -*- coding:utf-8 -*-
import torch
import numpy as np
'''
用逻辑回归实现异或功能
'''
# 加载数据
data = torch.tensor([[1,1],[1,0],[0,1],[0,0]],dtype=torch.float)
label = torch.tensor([[0,1,1,0]],dtype=torch.float).t()
w = torch.tensor([1,1],dtype=torch.float, requires_grad=True)

# 加载一个数据,代入到公式中
i = 0
x = data[i]
#f = 1/[1+e^(-wx)]
f = 1/(1+torch.pow(np.e,-x.dot(w)))
print(f) # 输出tensor(0.8808)

利用pytorch反向传播自动求导工具计算损失函数对参数w的导数

我们需要告诉pytorch损失函数,然后让它根据损失函数对参数w求导。接下来我们用代码实现。()

# -*- coding:utf-8 -*-
import torch
import numpy as np
'''
用逻辑回归实现异或功能
'''
# 加载数据
data = torch.tensor([[1,1],[1,0],[0,1],[0,0]],dtype=torch.float)
label = torch.tensor([[0,1,1,0]],dtype=torch.float).t()
w = torch.tensor([1,1],dtype=torch.float,requires_grad=True)


# 加载一个数据,代入到公式中
i = 0
x = data[i]
#f = 1/[1+e^(-wx)]
f = 1/(1+torch.pow(np.e,-x.dot(w)))
print(f) # 输出tensor(0.8808)

loss = (f-label[i])**2 
print(loss)# 输出:tensor([0.7758])
loss.backward() # 使用反向传播求损失函数对参数w的导数值
print(w.grad) # 损失函数对w的导数值tensor([0.1850, 0.1850])

利用反向传播的导数实现梯度下降

# -*- coding:utf-8 -*-
import torch
import numpy as np
'''
用逻辑回归实现异或功能
'''
# 加载数据
data = torch.tensor([[1,1],[1,0],[0,1],[0,0]],dtype=torch.float)
label = torch.tensor([[0,1,1,0]],dtype=torch.float).t()
w = torch.tensor([1,1],dtype=torch.float,requires_grad=True)


# 加载一个数据,代入到公式中
i = 0
x = data[i]
#f = 1/[1+e^(-wx)]
f = 1/(1+torch.pow(np.e,-x.dot(w)))
print(f) # 输出tensor(0.8808)

loss = (f-label[i])**2 
print(loss)# 输出:tensor([0.7758])
loss.backward() # 使用反向传播求损失函数对参数w的导数值
print(w.grad) # 损失函数对w的导数值tensor([0.1850, 0.1850])

learning_rate = 1e-2
w = w - learning_rate*w.grad
print('更新后的参数w',w)#更新后的参数w tensor([0.9982, 0.9982])

我们自己手动计算导数,并实现梯度下降

# -*- coding:utf-8 -*-
import torch
import numpy as np
'''
用逻辑回归实现异或功能
'''
# 加载数据
data = torch.tensor([[1,1],[1,0],[0,1],[0,0]],dtype=torch.float)
label = torch.tensor([[0,1,1,0]],dtype=torch.float).t()
w = torch.tensor([1,1],dtype=torch.float,requires_grad=True)


# 加载一个数据,代入到公式中
i = 0
x = data[i]
#f = 1/[1+e^(-wx)]
f = 1/(1+torch.pow(np.e,-x.dot(w)))
print(f) # 输出tensor(0.8808)

#loss = (f-label[i])**2 
# 计算损失函数loss对w的导数
# 我们把e^(-wx)这部分先存到一个变量里面
e_wx = torch.pow(np.e,-x.dot(w))
dloss_dw = 2*(f-label[i])*x*e_wx/(1+e_wx)**2
print(dloss_dw)
learning_rate = 1e-2
w = w - learning_rate*dloss_dw
print('更新后的参数w',w)#更新后的参数w tensor([0.9982, 0.9982])