让步比
即某一特定事件发生的概率,定义logit函数为让步比的log形式
l o g i t ( p ) = l o g p 1 − p logit(p) = log\frac{p}{1-p} logit(p)=log1−pp
我们可以用logit函数表示特征值和对数概率间的线性关系
l o g i t ( p ( y = 1 ∣ x ⃗ ) ) = w 0 x 0 + w 1 x 1 + . . . + w m x m = w ⃗ T x ⃗ logit(p(y=1 | \vec{x} )) = w_{0}x_{0} + w_{1}x_{1} + ... + w_{m}x_{m} = \vec{w}^{T}\vec{x} logit(p(y=1∣x))=w0x0+w1x1+...+wmxm=wTx
其中
l
o
g
i
t
(
p
(
y
=
1
∣
x
⃗
)
)
logit(p(y=1 | \vec{x} ))
logit(p(y=1∣x))表示给定特征
x
⃗
\vec{x}
x,某个特定样本属于类1的条件概率。
实际上,我们关心的是预测某个样本属于某个特定类的概率,它是logit函数的逆函数(sigmoid函数,下面讲)
为什么要用让步比?
侧重点不同。
让步比(Odds)和概率(Probability)是统计学中用于描述事件发生可能性的两种不同方式。而概率更直接反映事件发生的可能性大小,而让步比则强调的是事件发生的几率与不发生的几率之间的对比。
概率p的取值范围限制在0到1之间,而让步比
p
1
−
p
\frac{p}{1-p}
1−pp的取值范围是0到正无穷,这使得让步比在数学运算和建模中有时更方便(比如在逻辑回归模型中,让步比的对数可以映射到实数范围,便于模型拟合)。
sigmoid函数
ϕ ( z ) = 1 1 + e − z \phi (z) = \frac{1}{1+e^{-z}} ϕ(z)=1+e−z1
其中,z为净输入,z = w ⃗ T x ⃗ \vec{w}^{T}\vec{x} wTx
绘制图像如下:
在逻辑回归中,我们设定激活函数由原先的f(x)=x变成现在的sigmoid函数。
为什么要用sigmoid函数?
1.Sigmoid函数的输出值范围在0到1之间,这可以被解释为某一事件发生的概率,所以我们不仅可以知道分类结果,还可以知道其概率。
->在逻辑回归中,Sigmoid函数将线性模型的输出转换为概率形式,使得模型可以预测事件发生的可能性。
->当
ϕ
(
x
)
≥
0.5
\phi(x) \ge 0.5
ϕ(x)≥0.5,我们认为预测值
y
^
=
1
\hat{y} =1
y^=1,反之
y
^
=
0
\hat{y} =0
y^=0
2.在神经网络中,Sigmoid函数提供了非线性映射,使得网络能够学习和逼近复杂的函数。
3.出于自身性质,sigmoid函数求导非常方便,有
ϕ ′ ( x ) = ϕ ( x ) ( 1 − ϕ ( x ) ) \phi^{'} (x)=\phi(x)(1−\phi(x)) ϕ′(x)=ϕ(x)(1−ϕ(x))
重新定义代价函数
若继续使用 J ( w ⃗ ) = ∑ i 1 2 ( ϕ ( z ( i ) ) − y ( i ) ) 2 J(\vec{w} ) = \sum_{i}^{} \frac{1}{2} (\phi(z^{(i)}) - y^{(i)})^{2} J(w)=∑i21(ϕ(z(i))−y(i))2,会导致出现多个极小值点,无法梯度下降到最小值。
所以我们使用似然估计的方法,关于似然的知识可以参考以下文章:
假设数据集中每一个样本相互独立,则:
L
(
w
⃗
)
=
P
(
y
⃗
∣
x
⃗
;
w
⃗
)
=
∏
i
=
1
m
P
(
y
(
i
)
∣
x
(
i
)
;
w
⃗
)
=
∏
i
=
1
m
(
ϕ
(
z
(
i
)
)
)
y
(
i
)
(
1
−
ϕ
(
z
(
i
)
)
)
1
−
y
(
i
)
L(\vec{w}) = P(\vec{y}|\vec{x};\vec{w}) = \prod_{i=1}^{m}P(y^{(i)} | x^{(i)}; \vec{w}) = \prod_{i=1}^{m}(\phi(z^{(i)}))^{y(i)}(1-\phi(z^{(i)}))^{1-y(i)}
L(w)=P(y∣x;w)=∏i=1mP(y(i)∣x(i);w)=∏i=1m(ϕ(z(i)))y(i)(1−ϕ(z(i)))1−y(i)
显然这个函数不好求最小值,所以我们取对数似然:
l
(
w
⃗
)
=
l
o
g
L
(
w
⃗
)
=
∑
i
=
1
n
y
(
i
)
l
o
g
(
ϕ
(
z
(
i
)
)
)
+
∑
i
=
1
n
(
1
−
y
(
i
)
)
l
o
g
(
1
−
ϕ
(
z
(
i
)
)
)
l(\vec{w}) = logL(\vec{w}) = \sum_{i=1}^{n}y^{(i)}log(\phi(z^{(i)})) + \sum_{i=1}^{n}(1 - y^{(i)})log(1 - \phi(z^{(i)}))
l(w)=logL(w)=∑i=1ny(i)log(ϕ(z(i)))+∑i=1n(1−y(i))log(1−ϕ(z(i)))
然后就可以用梯度上升(类似梯度下降)的方法来求得最大似然
为什么要用这个当作代价函数?
可以看到,如果J(w)与y越接近,代价越接近0;反之,代价趋于无穷大。
在给定观测数据的情况下,似然用于描述参数值的可信度。故取得最大似然,就可以让这组
w
⃗
\vec{w}
w最可靠。
综上,这个最大似然估计函数起到了代价函数的效果。
梯度下降使用
为了“下降”,我们让
J
(
w
⃗
)
=
−
∑
i
=
1
n
y
(
i
)
l
o
g
(
ϕ
(
z
(
i
)
)
)
−
∑
i
=
1
n
(
1
−
y
(
i
)
)
l
o
g
(
1
−
ϕ
(
z
(
i
)
)
)
J(\vec{w}) = -\sum_{i=1}^{n}y^{(i)}log(\phi(z^{(i)})) - \sum_{i=1}^{n}(1 - y^{(i)})log(1 - \phi(z^{(i)}))
J(w)=−∑i=1ny(i)log(ϕ(z(i)))−∑i=1n(1−y(i))log(1−ϕ(z(i)))
与线性感知器相同,分别对b和
w
j
w_{j}
wj求偏导,利用梯度下降:
先求 ϕ \phi ϕ对z的偏导,有:
∂ ϕ ( z ) ∂ z = ∂ ∂ z 1 1 + e − z = e − z ( 1 + e − z ) 2 = ϕ ( z ) ( 1 − ϕ ( z ) ) \frac{\partial \phi(z)}{\partial z} = \frac{\partial }{\partial z}\frac{1}{1+e^{-z}} = \frac{e^{-z}}{(1+e^{-z})^{2}} = \phi(z)(1-\phi(z)) ∂z∂ϕ(z)=∂z∂1+e−z1=(1+e−z)2e−z=ϕ(z)(1−ϕ(z))
对于w有
∂ J ∂ w j = ( y ∗ 1 ϕ ( z ) − ( 1 − y ) ∗ 1 1 − ϕ ( z ) ) ∗ ∂ ϕ ( z ) ∂ w j \frac{\partial J}{\partial w_{j}} = (y * \frac{1}{\phi(z)} - (1-y) * \frac{1}{1 - \phi(z)}) * \frac{\partial \phi(z)}{\partial w_{j}} ∂wj∂J=(y∗ϕ(z)1−(1−y)∗1−ϕ(z)1)∗∂wj∂ϕ(z)
= ( y ∗ 1 ϕ ( z ) − ( 1 − y ) ∗ 1 1 − ϕ ( z ) ) ∗ ∂ ϕ ( z ) ∂ z ∗ ∂ z ∂ w j =(y * \frac{1}{\phi(z)} - (1-y) * \frac{1}{1 - \phi(z)}) * \frac{\partial \phi(z)}{\partial z} * \frac{\partial z}{\partial w_{j}} =(y∗ϕ(z)1−(1−y)∗1−ϕ(z)1)∗∂z∂ϕ(z)∗∂wj∂z
= ( y ∗ 1 ϕ ( z ) − ( 1 − y ) ∗ 1 1 − ϕ ( z ) ) ∗ ϕ ( z ) ( 1 − ϕ ( z ) ) ∗ ∂ z ∂ w j = (y * \frac{1}{\phi(z)} - (1-y) * \frac{1}{1 - \phi(z)}) * \phi(z)(1-\phi(z)) * \frac{\partial z}{\partial w_{j}} =(y∗ϕ(z)1−(1−y)∗1−ϕ(z)1)∗ϕ(z)(1−ϕ(z))∗∂wj∂z
= ( y − ϕ ( z ) ) ∗ x j = (y - \phi(z)) * x_{j} =(y−ϕ(z))∗xj
△ w ⃗ = η ▽ J ( w ⃗ ) \bigtriangleup \vec{w} = \eta \bigtriangledown J(\vec{w}) △w=η▽J(w)
△ w j = − η ∂ J ∂ w j = − η ∑ i = 1 n ( y ( i ) − ϕ ( z ( i ) ) ) x j ( i ) \bigtriangleup w_{j} = -\eta\frac{\partial J}{\partial w_{j}} = -\eta\sum_{i=1}^{n}(y^{(i)}-\phi(z^{(i)}))x^{(i)}_{j} △wj=−η∂wj∂J=−η∑i=1n(y(i)−ϕ(z(i)))xj(i)
同理,对于b有:
∂ J ∂ b = ( y − ϕ ( z ) ) ∗ ∂ z ∂ b \frac{\partial J}{\partial b} = (y - \phi(z)) * \frac{\partial z}{\partial b} ∂b∂J=(y−ϕ(z))∗∂b∂z
= y − ϕ ( z ) = y - \phi(z) =y−ϕ(z)
△ b = − η ∂ J ∂ b = − η ∑ i = 1 n ( y ( i ) − ϕ ( z ( i ) ) ) \bigtriangleup b = -\eta\frac{\partial J}{\partial b} = -\eta\sum_{i=1}^{n}(y^{(i)}-\phi(z^{(i)})) △b=−η∂b∂J=−η∑i=1n(y(i)−ϕ(z(i)))
此外,为了不同大小的训练集之间的代价函数值可以互相比较,一般会在J上除以m,求得平均损失即:
J ( w ⃗ ) = − 1 m ∑ i = 1 n y ( i ) l o g ( ϕ ( z ( i ) ) ) − 1 m ∑ i = 1 n ( 1 − y ( i ) ) l o g ( 1 − ϕ ( z ( i ) ) ) J(\vec{w}) = -\frac{1}{m}\sum_{i=1}^{n}y^{(i)}log(\phi(z^{(i)})) - \frac{1}{m}\sum_{i=1}^{n}(1 - y^{(i)})log(1 - \phi(z^{(i)})) J(w)=−m1∑i=1ny(i)log(ϕ(z(i)))−m1∑i=1n(1−y(i))log(1−ϕ(z(i)))
那么 △ w j \bigtriangleup w_{j} △wj和 △ b \bigtriangleup b △b相应除以m即可
用scikit-learn训练
注意,该模型只能用于二分类,所以我们只考虑Iris-setosa和Iris-versicolor两种花,对应0和1
# 导入必要的库和工具
from matplotlib.colors import ListedColormap # 用于创建颜色图
import matplotlib.pyplot as plt # 用于绘图
import numpy as np # 用于数值计算
from distutils.version import LooseVersion # 用于版本号的比较
import matplotlib # 用于绘图的库
from sklearn.model_selection import train_test_split # 用于数据集的划分
from sklearn.preprocessing import StandardScaler # 用于特征标准化
from sklearn.linear_model import Perceptron # 感知机分类器
from sklearn import datasets # 用于获取数据集
# 加载鸢尾花数据集
iris = datasets.load_iris()
X = iris.data[:, [2, 3]] # 选取花瓣长度和花瓣宽度作为特征
y = iris.target # 目标变量(类别标签)
# 将数据集划分为训练集和测试集
X_train, X_test, y_train, y_test = train_test_split(
X, y, test_size=0.3, random_state=1, stratify=y)
# test_size=0.3表示测试集占30%,stratify=y确保训练集和测试集中的类别分布与原始数据集一致
# random_state=1是为了每次运行时都能得到相同的结果,便于复现
# 特征标准化
# 通过计算训练集的平均值和标准差来标准化数据
sc = StandardScaler()
sc.fit(X_train) # 计算X_train的平均值和标准差
X_train_std = sc.transform(X_train) # 使用计算出的平均值和标准差对X_train进行标准化
X_test_std = sc.transform(X_test) # 使用相同的平均值和标准差对X_test进行标准化
# 创建并训练感知机模型
ppn = Perceptron(eta0=0.1, random_state=1) # eta0是学习率,random_state是随机种子
ppn.fit(X_train_std, y_train) # 使用标准化后的训练集对感知机模型进行训练
def plot_decision_regions(X, y, classifier, test_idx=None, resolution=0.02):
# 设置标记和颜色
markers = ('s', 'x', 'o', '^', 'v') # 不同类别的标记符号
colors = ('red', 'blue', 'lightgreen', 'gray', 'cyan') # 不同类别的颜色
cmap = ListedColormap(colors[:len(np.unique(y))]) # 创建一个颜色图,长度等于类别数
# 生成决策边界的网格
x1_min, x1_max = X[:, 0].min() - 1, X[:, 0].max() + 1 # 第一个特征的范围
x2_min, x2_max = X[:, 1].min() - 1, X[:, 1].max() + 1 # 第二个特征的范围
xx1, xx2 = np.meshgrid(np.arange(x1_min, x1_max, resolution),
np.arange(x2_min, x2_max, resolution)) # 构建网格
Z = classifier.predict(np.array([xx1.ravel(), xx2.ravel()]).T) # 预测网格上每个点的类别
Z = Z.reshape(xx1.shape) # 重塑预测结果形状
# 绘制决策边界
plt.contourf(xx1, xx2, Z, alpha=0.3, cmap=cmap) # 填充颜色的决策边界
plt.xlim(xx1.min(), xx1.max()) # 设置x轴的范围
plt.ylim(xx2.min(), xx2.max()) # 设置y轴的范围
# 绘制训练数据点
for idx, cl in enumerate(np.unique(y)): # 遍历每个类别
plt.scatter(x=X[y == cl, 0], # 类别为cl的x1数据点
y=X[y == cl, 1], # 类别为cl的x2数据点
alpha=0.8, # 设置透明度
color=colors[idx], # 使用对应颜色
marker=markers[idx], # 使用对应标记
label=cl, # 设置图例标签
edgecolor='black') # 设置边框颜色
# 如果存在测试集,突出显示测试数据点
if test_idx is not None:
X_test, y_test = X[test_idx, :], y[test_idx] # 分离测试集的特征和标签
if LooseVersion(matplotlib.__version__) < LooseVersion('0.3.4'): # 检查matplotlib版本
plt.scatter(X_test[:, 0],
X_test[:, 1],
c='',
edgecolor='black',
alpha=1.0,
linewidth=1,
marker='o',
s=100,
label='test set')
else: # 对于更现代的matplotlib版本,使用'c=None'而不是'c=""'
plt.scatter(X_test[:, 0],
X_test[:, 1],
c='none',
edgecolor='black',
alpha=1.0,
linewidth=1,
marker='o',
s=100,
label='test set')
# 将训练集和测试集的标准化特征数据堆叠在一起
X_combined_std = np.vstack((X_train_std, X_test_std)) # 使用numpy的vstack函数沿垂直轴堆叠矩阵
# 将训练集和测试集的标签数据拼接在一起
y_combined = np.hstack((y_train, y_test)) # 使用numpy的hstack函数沿水平轴拼接数组
# 调用plot_decision_regions函数绘图
# 注意:这里假设plot_decision_regions是之前定义的自定义函数,用于可视化分类器的决策边界
plot_decision_regions(X=X_combined_std, y=y_combined,
classifier=ppn, # 这里的ppn假设是训练好的分类器对象
test_idx=range(105, 150)) # 指定测试集的样本索引,用于突出显示在图中
# 设置x轴的标签
plt.xlabel('petal length [standardized]') # x轴标签为“花瓣长度[标准化]”
# 设置y轴的标签
plt.ylabel('petal width [standardized]') # y轴标签为“花瓣宽度[标准化]”
# 添加图例,并设置其位置
plt.legend(loc='upper left') # 图例位于左上角
# 调整布局,确保图例不会被裁剪
plt.tight_layout() # 自动调整子图参数以填充整个图像区域
# 保存图片(注释掉的代码)
# plt.savefig('images/03_01.png', dpi=300) # 将图像保存为PNG文件,分辨率为300dpi
# 显示图像
plt.show() # 绘制并显示图像