利用pytorch和交叉熵损失函数原理完成Iris分类器构造
一 .基于对逻辑回归的数学基础
-
从分类开始谈起
某个样本属于A类还是B类,从结果上讲就是值为0,还是值为1。但影响这个分类的是由一些因素决定的。我们从数学角度可以使用向量表示这些因素(这些因素就影响某个样本属于A类还是B类):
x = ( x 1 , x 2 , … , x n ) x=(x_1,x_2,\dots,x_n) x=(x1,x2,…,xn)
其中 x x x就是表示一个样本,样本 x x x具有n个影响分类的特征。如果考虑偏置项,则可以增加一个份量1。
x = ( x 1 , x 2 , … , x n , 1 ) x=(x_1,x_2,\dots,x_n,1) x=(x1,x2,…,xn,1) -
建立分类的逻辑模型
我们假设有两套标准判定样本所属的分类,使用数学函数表示如下:
|- y A = f ( x ) y_A=f(x) yA=f(x) 样本x属于A的可能性;
|- y B = g ( x ) y_B=g(x) yB=g(x) 样本x属于B的可能性;
这样我们就可以建立一个分类模型:
y = { 1 , y A > y B 0 , y A ⩽ y B y=\begin{cases} 1,\quad y_A>y_B\\ 0,\quad y_A\leqslant y_B\\ \end{cases} y={1,yA>yB0,yA⩽yB
当 y = 1 y=1 y=1,则样本 x x x属于A类;当 y = 0 y=0 y=0,则样本 x x x属于B类;
可以把上述模型表示为:
y = { 1 , y A − y B > 0 0 , 其 他 y=\begin{cases} 1,\quad y_A-y_B>0\\ 0,\quad 其他\\ \end{cases} y={1,yA−yB>00,其他 -
分类逻辑模型的概率分析基础
如果假设 y A , y B y_A,y_B yA,yB与 x x x是线性关系,同时考虑 y A , y B y_A,y_B yA,yB的误差都独立服从正态分布,可以把 y A , y B y_A,y_B yA,yB表示如下:
|- y A = x W A + ϵ A y_A=xW_A+\epsilon_A yA=xWA+ϵA
|- y B = x W B + ϵ B y_B=xW_B+\epsilon_B yB=xWB+ϵB
其中 ϵ A , ϵ B \epsilon_A,\epsilon_B ϵA,ϵB是服从独立分布的误差项,可以假设服从正态分布。
记 z = y A − y B z=y_A-y_B z=yA−yB,则:
|- z = x ( W A − W B ) + ( ϵ A − ϵ B ) z=x(W_A-W_B)+(\epsilon_A-\epsilon_B) z=x(WA−WB)+(ϵA−ϵB)
从而分类逻辑模型可以表示如下:
|- y = { 1 , z > 0 0 , 其 他 y=\begin{cases} 1,\quad z>0\\ 0,\quad 其他\\ \end{cases} y={1,z>00,其他
其中 W = W A − W B , ϵ = ϵ A − ϵ B W=W_A-W_B,\epsilon=\epsilon_A-\epsilon_B W=WA−WB,ϵ=ϵA−ϵB
z = x W + ϵ z=xW+\epsilon z=xW+ϵ
则样本X属于A的概率可以表示为:
|- P ( y = 1 ) = P ( z > 0 ) = P ( x W + ϵ > 0 ) = P ( ϵ > − x W ) P(y=1)=P(z>0)=P(xW+\epsilon>0)=P(\epsilon>-xW) P(y=1)=P(z>0)=P(xW+ϵ>0)=P(ϵ>−xW)
从正态分布可以继续推导:
|- P ( y = 1 ) = 1 − P ( ϵ ⩽ − x W ) = 1 − F ϵ ( − x W ) P(y=1)=1-P(\epsilon\leqslant-xW)=1-F_{\epsilon}(-xW) P(y=1)=1−P(ϵ⩽−xW)=1−Fϵ(−xW)
其中 F ϵ F_{\epsilon} Fϵ是变量 ϵ \epsilon ϵ的累积分布函数; P ( y = 1 ) P(y=1) P(y=1)表示样本属于A的概率 -
probit模型
上述推导的公式在学术上称为probit模型,建立的回归模型也称proit回归。
P ( y = 1 ) = 1 − F ϵ ( − x W ) P(y=1)=1-F_{\epsilon}(-xW) P(y=1)=1−Fϵ(−xW)
F ϵ ( − x W ) F_{\epsilon}(-xW) Fϵ(−xW)是正态分布函数的累积函数,上述累积分布函数,在服从正态分布的时候比较麻烦,因为正态分布的累积函数还没有解析表达式能够表达。 从而其参数估计非常麻烦,如果需要应用,则需要简化( 做近似处理 )。
为了解决正态分布累积函数的问题,正态分布的累积函数的计算居然是通过查表的形式提供运算结果,大家如果想不起,可以查阅自己的高中数据或者大学数学书。 -
正态分布的近似处理
正态分布表示:
p ( x ) = 1 2 π σ e x p ( − ( x − μ ) 2 2 σ 2 ) p(x)=\dfrac{1}{\sqrt{2\pi}\sigma}exp(-\dfrac{(x-\mu)^2}{2\sigma^2}) p(x)=2πσ1exp(−2σ2(x−μ)2) 其中:μ表示期望(均数),σ表示标准差,σ2表示方差
记正态分布为 N ( μ , σ ) N(μ,σ) N(μ,σ) ,标准正态分布是 N ( 0 , 1 ) N( 0,1 ) N(0,1)
我们可以简化下正态分布累积函数的表示:
F ϵ ( x ) = ϕ ( x − μ σ ) F_{\epsilon}(x)=\phi(\dfrac{x-\mu}{\sigma}) Fϵ(x)=ϕ(σx−μ)
因为 x − μ σ \dfrac{x-\mu}{\sigma} σx−μ是线性,所以只需要考虑标准正态分布。
在数学上存在一个逻辑分布,与正态分布非常相似, -
逻辑分布与标准正态分布
下面使用可视化来认识下逻辑分布函数与正态分布函数的近似度。
|- 正态分布: p ( x ) = 1 2 π σ e x p ( − ( x − μ ) 2 2 σ 2 ) p(x)=\dfrac{1}{\sqrt{2\pi}\sigma}exp(-\dfrac{(x-\mu)^2}{2\sigma^2}) p(x)=2πσ1exp(−2σ2(x−μ)2)
|- 逻辑分布: p ( x ) = e − x ( 1 + e − x ) 2 p(x)=\dfrac{e^{-x}}{(1+e^{-x})^2} p(x)=(1+e−x)2e−x
逻辑分布函数与正态分布的区别就在于:逻辑分布有累积函数,其累积函数如下:
|- 逻辑分布累积函数: S ( x ) = 1 1 + e − x S(x)=\dfrac{1}{1+e^{-x}} S(x)=1+e−x1
下面是逻辑分布函数,正态分布函数的比较:
% matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
# 绘图的坐标轴
figure=plt.figure('正态分布与逻辑分布', figsize=(10, 4))
ax = figure.add_axes([0.1, 0.1, 0.8, 0.8], label='逻辑分布与正态分布')
ax.set_xlabel('x值')
ax.set_ylabel('y值')
# 设置绘制曲线范围
ax.set_xlim(left=-10,right=10) # x取值范围
ax.set_ylim(bottom=0, top=0.5) # 概率在[0, 1) 之间
# 标准正态分布函数--------------------
# x取值范围
x_n=np.linspace( -10 ,10, 100, dtype=np.float32 )
# 系数-方差
sigma=1
# 系数-均值
mu=0
# 正态分布常数系数
coefficient = 1.0 / ( np.sqrt( 2 * np.pi) * sigma )
# 正态分布指数
exponent = -(x_n-mu)**2/(2*sigma**2)
y_n=coefficient * np.exp( exponent )
ax.plot(x_n, y_n,color='r',label='正态分布')
# 逻辑分布函数--------------------
x_l=np.linspace( -10 ,10, 100, dtype=np.float32 )
y_l=np.exp( -x_l ) / ( 1 + np.exp( -x_l ) )**2
ax.plot(x_l, y_l,color='b',label='逻辑分布')
ax.legend()
figure.show(warn=False)
- 逻辑分布与标准正态分布的累积函数
标准正态分布没有累积函数,所以我们采用scipy的积分函数来绘制。
使用积分表示正态分布累积函数的公式如下:
|- F ϵ ( x ) = ∫ − ∞ x 1 2 π σ e x p ( − ( t − μ ) 2 2 σ 2 ) d t F_{\epsilon}(x)=\int_{-\infty}^{x}\dfrac{1}{\sqrt{2\pi}\sigma}exp(-\dfrac{(t-\mu)^2}{2\sigma^2})\mathrm{d} t Fϵ(x)=∫−∞x2πσ1exp(−2σ2(t−μ)2)dt,
% matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import scipy.integrate as integrate
# 定义正态函数,用于积分运算
def normal(t,m,s):
# 系数-方差
sigma=s
# 系数-均值
mu=m
# 正态分布常数系数
coefficient = 1.0 / ( np.sqrt( 2 * np.pi) * sigma )
# 正态分布指数
exponent = -(t-mu)**2/(2*sigma**2)
re=coefficient * np.exp( exponent )
return re
def cumulative(x):
re=integrate.quad(
normal, # 积分函数
-np.inf, # 积分下限
x, # 积分上限
args=(0.0, 1.0) # 传递给函数的参数(除第一个参数外,按照顺序来)
)
return re[0] #第一个是积分,第二个是误差上限
# 绘图的坐标轴
figure=plt.figure('正态分布与逻辑分布', figsize=(10, 4))
ax = figure.add_axes([0.1, 0.1, 0.8, 0.8], label='逻辑分布与正态分布')
ax.set_xlabel('x值')
ax.set_ylabel('y值')
# 设置绘制曲线范围
ax.set_xlim(left=-10,right=10) # x取值范围
ax.set_ylim(bottom=-0.1, top=1.1) # 概率在[0, 1) 之间
# 标准正态分布函数--------------------
# x取值范围
x_n=np.linspace( -10 ,10, 100, dtype=np.float32 )
y_n=[cumulative(x) for x in x_n ]
ax.plot(x_n, y_n,color='r',label='正态分布累积函数')
# 逻辑分布函数--------------------
x_l=np.linspace( -10 ,10, 100, dtype=np.float32 )
y_l=1.0 / ( 1 + np.exp( -x_l ) )
ax.plot(x_l, y_l,color='b',label='逻辑分布累积函数')
ax.legend()
ax.grid(b=True)
figure.show(warn=False)
- 最佳的逻辑分布
如果逻辑分布参数做一些调整,可以最佳接近。下面是最佳逻辑分布累积函数表示:
|- S ( x ) = 1 1 + e − 1.702 x S(x)=\dfrac{1}{1+e^{-1.702x}} S(x)=1+e−1.702x1
下面是可视化后的直观效果。
% matplotlib inline
import matplotlib.pyplot as plt
import numpy as np
import scipy.integrate as integrate
# 定义正态函数,用于积分运算
def normal(t,m,s):
# 系数-方差
sigma=s
# 系数-均值
mu=m
# 正态分布常数系数
coefficient = 1.0 / ( np.sqrt( 2 * np.pi) * sigma )
# 正态分布指数
exponent = -(t-mu)**2/(2*sigma**2)
re=coefficient * np.exp( exponent )
return re
def cumulative(x):
re=integrate.quad(
normal, # 积分函数
-np.inf, # 积分下限
x, # 积分上限
args=(0.0, 1.0) # 传递给函数的参数(除第一个参数外,按照顺序来)
)
return re[0] #第一个是积分,第二个是误差上限
# 绘图的坐标轴
figure=plt.figure('正态分布与逻辑分布', figsize=(10, 4))
ax = figure.add_axes([0.1, 0.1, 0.8, 0.8], label='逻辑分布与正态分布')
ax.set_xlabel('x值')
ax.set_ylabel('y值')
# 设置绘制曲线范围
ax.set_xlim(left=-10,right=10) # x取值范围
ax.set_ylim(bottom=-0.1, top=1.1) # 概率在[0, 1) 之间
# 标准正态分布函数--------------------
# x取值范围
x_n=np.linspace( -10 ,10, 100, dtype=np.float32 )
y_n=[cumulative(x) for x in x_n ]
ax.plot(x_n, y_n,color='r',label='正态分布累积函数 $F_{\epsilon}(x)=\int_{-\infty}^{x}\dfrac{1}{\sqrt{2\pi}\sigma}exp(-\dfrac{(t-\mu)^2}{2\sigma^2})\mathrm{d} t$')
# 逻辑分布函数--------------------
x_l=np.linspace( -10 ,10, 100, dtype=np.float32 )
y_l=1.0 / ( 1 + np.exp( -1.702*x_l ) )
ax.plot(x_l, y_l,color='b',label='逻辑分布累积函数 $S(x)=\dfrac{1}{1+e^{-1.702x}}$ ')
ax.legend()
ax.grid(b=True)
figure.show(warn=False)
9.sigmoid函数
S
(
x
)
=
1
1
+
e
−
1.702
x
S(x)=\dfrac{1}{1+e^{-1.702x}}
S(x)=1+e−1.702x1 就是鼎鼎大名的sigmoid函数,该函数具备许多良好的性质。尤其是深度学习中的大杀器!经常会用到。
函数的特点:
|- 函数的导数可以用自身表示:
S
′
(
x
)
=
S
(
x
)
(
1
−
S
(
x
)
)
S^\prime(x)=S(x)(1-S(x))
S′(x)=S(x)(1−S(x))
|- 连续处处可微
|- 值在[0,1)范围
|- 单调递增
|- 非线性
|- 平滑性
|- 原点附近近似identity(f(x)≈x)或者先线性性。
函数的缺点:
|- 运算量大(后面使用梯度下降算法可以提现出来,尤其误差传递的时候)
|- 容易出现梯度消失的情况,从而不利于样本训练,甚至完不成梯度训练。(大家可以从逻辑分布函数看得出来,sigmoid函数的导数就是逻辑分布函数,逻辑分布函数从0开始,就逐步趋向于0,容易产生梯度消失)
二. 利用pytorch和numpy模块对数据集进行处理
模型
-
预测:
- y = S ( x W + b ) y = S(xW + b) y=S(xW+b)
-
最好 W , b W, b W,b就是是下面损失函数最小的 W , b W, b W,b
- 损失函数:交叉熵函数
实现
-
加载数据集
-
定义学习的w, b
循环
- 计算预测值
- 使用交叉熵计算损失值
- 求导数
- 更新w,b
from sklearn.datasets import load_iris
import numpy
import torch
# 加载数据集
data, target = load_iris(return_X_y=True)
# x = torch.from_numpy(data[0:100]).float()
x = torch.Tensor(data[:100]) # FloatTensor = Tensor
y = torch.Tensor(target[:100]).view(100, 1)
# 定义学习参数
w = torch.randn(1, 4)
b = torch.randn(1)
w.requires_grad= True
b.requires_grad= True
# 超参数
epoch = 500000
lr = 0.0001
for n in range(epoch):
# 预测
# y_ = w @ x.T + b
y_ = torch.nn.functional.linear(x, weight=w, bias=b)
y_ = torch.sigmoid(y_)
# 计算损失
loss = torch.nn.functional.binary_cross_entropy(y_, y)
# 求导
loss.backward()
# 更新
with torch.no_grad():
w -= lr * w.grad
b -= lr * b.grad
# 清空导数
w.grad.zero_()
b.grad.zero_()
y_[ y_ > 0.5] = 1
y_[ y_ <= 0.5] = 0
corr_rate = (y == y_).float().mean()
if n % 100 ==0:
print(f"损失值:{loss:8.6f}, 正确率:{corr_rate * 100: 8.2f}")
结果如下