构建完整的分类项目
- 收集数据集并选择合适的特征
- 选择度量模型性能的指标
- 选择具体的模型进行训练
- 评估模型性能并调参
基于概率的分类模型
线性判别分析
基于贝叶斯公式
通过贝叶斯定理计算贝叶斯公式的分子,比较分子最大的那个类别就是最终类别。
{
δ
k
(
x
)
=
l
n
(
g
k
(
x
)
)
=
l
n
π
k
+
μ
σ
2
x
−
μ
2
2
σ
2
μ
^
k
=
1
n
k
∑
i
:
y
i
=
k
x
i
σ
^
2
=
1
n
−
K
∑
k
=
1
K
∑
i
:
y
i
=
k
(
x
i
−
μ
^
k
)
2
{\begin{cases}\delta_k(x) = ln(g_k(x))=ln\pi_k+\dfrac{\mu}{\sigma^2}x-\dfrac{\mu^2}{2\sigma^2}\\{\hat{\mu}_k =\dfrac{1}{n_k}\sum\limits_{i:y_i=k}x_i}\\{\hat{\sigma}^2 =\dfrac{1}{n-K}\sum\limits_{k=1}^K\sum\limits_{i:y_i=k}(x_i-\hat{\mu}_k)^2}\end{cases}}
⎩⎪⎪⎪⎪⎪⎪⎨⎪⎪⎪⎪⎪⎪⎧δk(x)=ln(gk(x))=lnπk+σ2μx−2σ2μ2μ^k=nk1i:yi=k∑xiσ^2=n−K1k=1∑Ki:yi=k∑(xi−μ^k)2
基于降维分类
类内方差小,类间方差大
J
(
w
)
=
(
z
ˉ
1
−
z
ˉ
2
)
2
s
1
+
s
2
=
w
T
(
x
ˉ
c
1
−
x
ˉ
c
2
)
(
x
ˉ
c
1
−
x
ˉ
c
2
)
T
w
w
T
(
s
c
1
+
s
c
2
)
w
w
^
=
a
r
g
m
a
x
w
J
(
w
)
J(w) = \frac{(\bar{z}_1-\bar{z}_2)^2}{s_1+s_2} = \frac{w^T(\bar{x}_{c_1}-\bar{x}_{c_2})(\bar{x}_{c_1}-\bar{x}_{c_2})^Tw}{w^T(s_{c_1}+s_{c_2})w}\\ \;\;\; \hat{w} = argmax_w\;J(w)
J(w)=s1+s2(zˉ1−zˉ2)2=wT(sc1+sc2)wwT(xˉc1−xˉc2)(xˉc1−xˉc2)Tww^=argmaxwJ(w)
记:
S
b
=
(
x
ˉ
c
1
−
x
ˉ
c
2
)
(
x
ˉ
c
1
−
x
ˉ
c
2
)
T
,
S
w
=
(
s
c
1
+
s
c
2
)
S_b = (\bar{x}_{c_1}-\bar{x}_{c_2})(\bar{x}_{c_1}-\bar{x}_{c_2})^T,\;S_w = (s_{c_1}+s_{c_2})
Sb=(xˉc1−xˉc2)(xˉc1−xˉc2)T,Sw=(sc1+sc2),因此
J
(
w
)
=
w
T
S
b
w
w
T
S
w
w
J(w) = \frac{w^TS_bw}{w^TS_ww}
J(w)=wTSwwwTSbw
w
=
S
w
−
1
(
x
ˉ
c
1
−
x
ˉ
c
2
)
w = S_w^{-1}(\bar{x}_{c_1}-\bar{x}_{c_2})
w=Sw−1(xˉc1−xˉc2)
决策树
基尼系数
G = ∑ k = 1 K p ^ m k ( 1 − p ^ m k ) G = \sum\limits_{k=1}^{K} \hat{p}_{mk}(1-\hat{p}_{mk}) G=k=1∑Kp^mk(1−p^mk)
衡量节点纯度,节点纯度越高,基尼系数越小
交叉熵
D = − ∑ k = 1 K p ^ m k l o g p ^ m k D = -\sum\limits_{k=1}^{K} \hat{p}_{mk}log\;\hat{p}_{mk} D=−k=1∑Kp^mklogp^mk
节点纯度越高,交叉熵越小
决策树分类算法
a. 选择最优切分特征j以及该特征上的最优点s:
遍历特征j以及固定j后遍历切分点s,选择使得基尼系数或者交叉熵最小的(j,s)
b.按照(j,s)分裂特征空间,每个区域内的类别为该区域内样本比例最多的类别。
c. 继续调用步骤1,2直到满足停止条件,就是每个区域的样本数小于等于5。
d. 将特征空间划分为J个不同的区域,生成分类树。
支持向量机SVM
min w , b 1 2 ∥ w ∥ 2 s.t. y ( i ) ( w T x ( i ) + b ) ≥ 1 , i = 1 , … , n \begin{aligned} \min _{w, b} & \frac{1}{2}\|w\|^{2} \\ \text { s.t. } & y^{(i)}\left(w^{T} x^{(i)}+b\right) \geq 1, \quad i=1, \ldots, n \end{aligned} w,bmin s.t. 21∥w∥2y(i)(wTx(i)+b)≥1,i=1,…,n
非线性支持向量机
将数据投影到更高的维度、
核函数
假设
ϕ
\phi
ϕ是一个从低维的输入空间
χ
\chi
χ(欧式空间的子集或者离散集合)到高维的希尔伯特空间的
H
\mathcal{H}
H映射。那么如果存在函数
K
(
x
,
z
)
K(x,z)
K(x,z),对于任意
x
,
z
∈
χ
x, z \in \chi
x,z∈χ,都有:
K
(
x
,
z
)
=
ϕ
(
x
)
∙
ϕ
(
z
)
K(x, z) = \phi(x) \bullet \phi(z)
K(x,z)=ϕ(x)∙ϕ(z)
那么我们就称
K
(
x
,
z
)
K(x, z)
K(x,z)为核函数。
多项式核函数
Polynomial Kernel是线性不可分SVM常用的核函数之一
K
(
x
i
,
x
j
)
=
(
⟨
x
i
,
x
j
⟩
+
c
)
d
K\left(\mathbf{x}_{i}, \mathbf{x}_{j}\right)=\left(\left\langle\mathbf{x}_{i}, \mathbf{x}_{j}\right\rangle+c\right)^{d}
K(xi,xj)=(⟨xi,xj⟩+c)d
高斯核函数
Gaussian Kernel,在SVM中也称为径向基核函数(Radial Basis Function,RBF),它是非线性分类SVM最主流的核函数
K
(
x
i
,
x
j
)
=
exp
(
−
∥
x
i
−
x
j
∥
2
2
2
σ
2
)
K\left(\mathbf{x}_{i}, \mathbf{x}_{j}\right)=\exp \left(-\frac{\left\|\mathbf{x}_{i}-\mathbf{x}_{j}\right\|_{2}^{2}}{2 \sigma^{2}}\right)
K(xi,xj)=exp(−2σ2∥xi−xj∥22)
使用高斯核函数之前需要将特征标准化
Sigmoid核函数
Sigmoid Kernel是线性不可分SVM常用的核函数之一
K
(
x
i
,
x
j
)
=
tanh
(
α
x
i
⊤
x
j
+
c
)
K\left(\mathbf{x}_{i}, \mathbf{x}_{j}\right)=\tanh \left(\alpha \mathbf{x}_{i}^{\top} \mathbf{x}_{j}+c\right)
K(xi,xj)=tanh(αxi⊤xj+c)
余弦相似度核
常用于衡量两段文字的余弦相似度
K
(
x
i
,
x
j
)
=
x
i
⊤
x
j
∥
x
i
∥
∥
x
j
∥
K\left(\mathbf{x}_{i}, \mathbf{x}_{j}\right)=\frac{\mathbf{x}_{i}^{\top} \mathbf{x}_{j}}{\left\|\mathbf{x}_{i}\right\|\left\|\mathbf{x}_{j}\right\|}
K(xi,xj)=∥xi∥∥xj∥xi⊤xj
作业
1.回归问题和分类问题的区别和联系
回归问题和分类问题需要预测的因变量不一样,回归问题的因变量是连续性变量,但是分类问题预测的因变量往往是离散集合中的某个元素。但是我们可以通过构造函数将回归问题的结果转化为分类问题的类别,logistics回归就是这样的模型,将自变量通过logistics函数转化为0到1的连续值,将0.5作为分类的界限。
2.为什么分类问题的损失函数是交叉熵而不是均方误差
在分类问题中,因变量是类别变量不是连续变量,以逻辑回归为例,使用均方误差时模型参数w会学习得非常慢,而使用交叉熵则有更快的学习速度
3.线性判别分析与逻辑回归在估计参数方面的异同点
逻辑回归的线性函数的系数是通过极大似然估计得到的,而线性回归分析的系数是类均值、方差的韩束,是通过估计训练样本的类均值和方差得到的
4.实现逻辑回归
逻辑回归
推荐运行环境:python 3.6
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
# import seaborn as sns
# plt.style.use('fivethirtyeight') #样式美化
import matplotlib.pyplot as plt
# import tensorflow as tf
from sklearn.metrics import classification_report # 这个包是评价报告
准备数据
读取数据
data = pd.read_csv('ex2data1.txt', names=['exam1', 'exam2', 'admitted'])
data.head() # 看前五行
exam1 | exam2 | admitted | |
---|---|---|---|
0 | 34.623660 | 78.024693 | 0 |
1 | 30.286711 | 43.894998 | 0 |
2 | 35.847409 | 72.902198 | 0 |
3 | 60.182599 | 86.308552 | 1 |
4 | 79.032736 | 75.344376 | 1 |
data.describe()
exam1 | exam2 | admitted | |
---|---|---|---|
count | 100.000000 | 100.000000 | 100.000000 |
mean | 65.644274 | 66.221998 | 0.600000 |
std | 19.458222 | 18.582783 | 0.492366 |
min | 30.058822 | 30.603263 | 0.000000 |
25% | 50.919511 | 48.179205 | 0.000000 |
50% | 67.032988 | 67.682381 | 1.000000 |
75% | 80.212529 | 79.360605 | 1.000000 |
max | 99.827858 | 98.869436 | 1.000000 |
处理数据
# x1 = data["exam1"]
data_0 = data[data["admitted"] == 0]
# data_0
data_1 = data[data["admitted"] == 1]
数据可视化
fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(data_0["exam1"], data_0["exam2"], color="red", label="0")
ax.scatter(data_1["exam1"], data_1["exam2"],
color="green", alpha=0.5, label="1")
ax.set_title('data')
ax.set_xlabel('exam1')
ax.set_ylabel('exam2')
# ax.grid(True)
plt.legend()
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6SMuiL0C-1626794545976)(D:/学习/计算机/机器学习/吴恩达机器学习/作业代码资料/编程作业-自写/exerise2_own/ML2logistic_regression/output_11_0.png)]
# sns.set(context="notebook", style="darkgrid", palette=sns.color_palette("RdBu", 2))
# sns.lmplot('exam1', 'exam2', hue='admitted', data=data,
# size=6,
# fit_reg=False,
# scatter_kws={"s": 50}
# )
# plt.show()#看下数据的样子
# exam1 = plt.plot(x1)
# exam2 = plt.plot(x2)
# plt.legend
定义数据处理函数
X = ( x 11 x 12 x 21 x 22 ⋮ ⋮ x m 1 x m 2 ) = ( x 1 T x 2 T x 3 T x 4 T ) X=\left( \begin{matrix} x_{11}& x_{12}\\ x_{21}& x_{22}\\ \vdots& \vdots\\ x_{m1}& x_{m2}\\ \end{matrix} \right) =\left( \begin{array}{c} \boldsymbol{x}_1^{\text{T}}\\ \boldsymbol{x}_{2}^{\text{T}}\\ \boldsymbol{x}_{3}^{\text{T}}\\ \boldsymbol{x}_{4}^{\text{T}}\\ \end{array} \right) X=⎝⎜⎜⎜⎛x11x21⋮xm1x12x22⋮xm2⎠⎟⎟⎟⎞=⎝⎜⎜⎛x1Tx2Tx3Tx4T⎠⎟⎟⎞
y = ( y 1 y 2 ⋮ y m ) \boldsymbol{y\,\,}=\,\,\left( \begin{array}{l} y_1\\ y_2\\ \vdots\\ y_m\\ \end{array} \right) y=⎝⎜⎜⎜⎛y1y2⋮ym⎠⎟⎟⎟⎞
将 X 扩展为 X ^ = ( X ; 1 ) , 即 X ^ = ( x 1 T 1 x 2 T 1 ⋮ ⋮ x 4 T 1 ) \text{将}X\text{扩展为}\hat{X}=\left( X;1 \right) ,\text{即}\hat{X}=\left( \begin{matrix} \boldsymbol{x}_{1}^{\text{T}}& 1\\ \boldsymbol{x}_{2}^{\text{T}}& 1\\ \vdots& \vdots\\ \boldsymbol{x}_{4}^{\text{T}}& 1\\ \end{matrix} \right) 将X扩展为X^=(X;1),即X^=⎝⎜⎜⎜⎛x1Tx2T⋮x4T11⋮1⎠⎟⎟⎟⎞
将 w 扩展为 w ^ = ( w b ) \text{将}\boldsymbol{w}\text{扩展为}\boldsymbol{\hat{w}}=\left( \begin{array}{c} \boldsymbol{w}\\ b\\ \end{array} \right) 将w扩展为w^=(wb)
因此 z = X w + b ⇒ z = X ^ w ( z 是所有的数据样本经过线性转化组成的向量 ) \text{因此}\mathbf{z}=X\boldsymbol{w}+b\Rightarrow \mathbf{z}=\hat{X}\boldsymbol{w}\left( \mathbf{z}\text{是所有的数据样本经过线性转化组成的向量} \right) 因此z=Xw+b⇒z=X^w(z是所有的数据样本经过线性转化组成的向量)
# 从整个数据中读取X
def get_X(df):
# """
# use concat to add intersect feature to avoid side effect
# not efficient for big dataset though
# """
# ones = pd.DataFrame({'ones': np.ones(len(df))})#ones是m行1列的dataframe
# data = pd.concat([ones, df], axis=1) # 合并数据,根据列合并
# return data.iloc[:, :-1].as_matrix() # 这个操作返回 ndarray,不是矩阵
df_X = df.iloc[:, :-1] # 取走除去y的一列作为原始的X
df_X["ones"] = 1 # 对X进行扩展
return df_X # 返回X
# 从整个数据中
def get_y(df):
# '''assume the last column is the target'''
return df.iloc[:, -1] # df.iloc[:, -1]是指df的最后一列,即y
特征归一化
def normalize_feature(df):
# """Applies function along input axis(default 0) of DataFrame."""
# return df.apply(lambda column: (column - column.mean()) / column.std())#特征缩放
df = (df - df.mean()) / df.std()
return df
变量赋值
X = get_X(data)
# X.head()
# print(X.shape)
X = np.array(X)#将X转换为NumPy形式
y = get_y(data)
# y.head()
print(y.shape)
y = np.array(y)#将y转换为NumPy形式
(100,)
# w = np.ones(3) # X(m*n) so theta is n*1
# np.matmul(X,w)
sigmoid 函数
σ ( z ) = 1 1 + e − z → z = w T x + b f w , b ( x ) = 1 1 + e − ( w T x + b ) { x 是一个数据样本 } \sigma \left( \text{z} \right) =\frac{1}{1+e^{-\text{z}}}\xrightarrow{\text{z}=\boldsymbol{w}^{\text{T}}\boldsymbol{x}+b}f_{w,b}\left( \boldsymbol{x} \right) =\frac{1}{1+e^{-\left( \boldsymbol{w}^{\text{T}}\boldsymbol{x}+b \right)}}\left\{ \boldsymbol{x}\text{是一个数据样本} \right\} σ(z)=1+e−z1z=wTx+bfw,b(x)=1+e−(wTx+b)1{x是一个数据样本}
def sigmoid(z):
# your code here (appro ~ 1 lines)
gz = 1/(1+np.exp(-z))
return gz
#调试sigmoid函数
# sigmoid(np.matmul(X,w))
# sigmoid(113.64835244)
下面程序会调用上面你写好的函数,并画出sigmoid函数图像。如果你的程序正确,你应该能在下方看到函数图像。
fig, ax = plt.subplots(figsize=(6, 4))
ax.plot(np.arange(-10, 10, step=0.01),
sigmoid(np.arange(-10, 10, step=0.01)))
ax.set_ylim((-0.1, 1.1))
ax.set_xlabel('z', fontsize=18)
ax.set_ylabel('g(z)', fontsize=18)
ax.set_title('sigmoid function', fontsize=18)
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-hFsEoTkH-1626794545979)(D:/学习/计算机/机器学习/吴恩达机器学习/作业代码资料/编程作业-自写/exerise2_own/ML2logistic_regression/output_28_0.png)]
cost function(代价函数)
- m a x ( ℓ ( θ ) ) = m i n ( − ℓ ( θ ) ) max(\ell(\theta)) = min(-\ell(\theta)) max(ℓ(θ))=min(−ℓ(θ))
- choose − ℓ ( θ ) -\ell(\theta) −ℓ(θ) as the cost function
− ln L ( w , b ) = 1 m ∑ i = 1 n − [ y ^ i ln f ( x i ) + ( 1 − y ^ i ) ln ( 1 − f ( x i ) ) ] -\ln L\left( w,b \right) =\frac{1}{m}\sum_{i=1}^n{-\left[ \hat{y}_i\ln f\left( x_i \right) +\left( 1-\hat{y}_i \right) \ln \left( 1-f\left( x_i \right) \right) \right]} −lnL(w,b)=m1i=1∑n−[y^ilnf(xi)+(1−y^i)ln(1−f(xi))]
w = np.zeros(3) # X(m*n) so theta is n*1
w
array([0., 0., 0.])
def cost(w, X, y):
costf = np.mean(-(y * np.log(sigmoid(np.matmul(X,w))) + (1 - y) * np.log(1 - sigmoid(np.matmul(X,w)))))
return costf
cost(w, X, y)
0.6931471805599453
如果你写的代码正确,这里的输出应该是0.6931471805599453
gradient descent(梯度下降)
求偏导
- 这是批量梯度下降(batch gradient descent)
∂ ( − ln L ( w , b ) ) ∂ w = ∑ i = 1 n − ( y ^ i − f ( x i ) ) x i \frac{\partial \left( -\ln L\left( w,b \right) \right)}{\partial w}=\sum_{i=1}^n{-\left( \hat{y}_i-f\left( x_i \right) \right) x_i} ∂w∂(−lnL(w,b))=i=1∑n−(y^i−f(xi))xi - 转化为向量化计算:
∂ ( − ln L ) ∂ w = 1 m X ^ T ( S i g m o i d ( X ^ w ) − y ) \frac{\partial \left( -\ln L \right)}{\partial \boldsymbol{w}}=\frac{1}{m}\hat{X}^{\text{T}}\left( Sigmoid\left( \hat{X}\boldsymbol{w} \right) -\boldsymbol{y} \right) ∂w∂(−lnL)=m1X^T(Sigmoid(X^w)−y)
def gradient(w, X, y):
grad = 1/len(y)*np.matmul(X.T,sigmoid(np.matmul(X,w))-y)
return grad
gradient(w, X, y)
array([-12.00921659, -11.26284221, -0.1 ])
优化参数
- 这里我使用
scipy.optimize.minimize
去寻找参数
import scipy.optimize as opt
res = opt.minimize(fun=cost, x0=w, args=(
X, y), method='Newton-CG', jac=gradient)
print(res)
fun: 0.20349770360438074
jac: array([-1.10543682e-04, 3.78579856e-04, 1.08434639e-06])
message: 'Optimization terminated successfully.'
nfev: 74
nhev: 0
nit: 30
njev: 246
status: 0
success: True
x: array([ 0.20626116, 0.20150139, -25.1650148 ])
用训练集预测和验证
预测函数
- 预测方法:
根据优化后的参数 w ,求得 f w , b ( x ) { ≥ 0.5 → 1 < 0.5 → 0 \text{根据优化后的参数}\boldsymbol{w}\text{,求得}f_{w,b}\left( \boldsymbol{x} \right) \left\{ \begin{array}{l} \ge 0.5\rightarrow 1\\ <0.5\rightarrow 0\\ \end{array} \right. 根据优化后的参数w,求得fw,b(x){≥0.5→1<0.5→0
def predict(x, w):
y_pred = sigmoid(np.matmul(X,w))
return (y_pred >= 0.5).astype(int)
final_w = res.x
y_pred = predict(X, final_w)
print(classification_report(y, y_pred))
precision recall f1-score support
0 0.87 0.85 0.86 40
1 0.90 0.92 0.91 60
accuracy 0.89 100
macro avg 0.89 0.88 0.88 100
weighted avg 0.89 0.89 0.89 100
寻找决策边界
http://stats.stackexchange.com/questions/93569/why-is-logistic-regression-a-linear-classifier
w
^
T
x
=
0
\boldsymbol{\hat{w}}^{\text{T}}\boldsymbol{x}=0
w^Tx=0
y = − w 1 w 2 x − b w 2 y=-\frac{w_1}{w_2}x-\frac{b}{w_2} y=−w2w1x−w2b
w ^ = ( w 1 w 2 b ) \boldsymbol{\hat{w}}=\left( \begin{array}{c} \boldsymbol{w}_1\\ \boldsymbol{w}_2\\ b\\ \end{array} \right) w^=⎝⎛w1w2b⎠⎞
print(res.x) # this is final theta
[ 0.20626116 0.20150139 -25.1650148 ]
# w_hat = -(res.x / res.x[1]) # find the equation
# print(coef)
#y=kx+b
w_hat = res.x
k=-(w_hat[0]/w_hat[1])
b=-(w_hat[2]/w_hat[1])
x = np.arange(130, step=0.1)
y = k*x + b
data.describe() # find the range of x and y
exam1 | exam2 | admitted | |
---|---|---|---|
count | 100.000000 | 100.000000 | 100.000000 |
mean | 65.644274 | 66.221998 | 0.600000 |
std | 19.458222 | 18.582783 | 0.492366 |
min | 30.058822 | 30.603263 | 0.000000 |
25% | 50.919511 | 48.179205 | 0.000000 |
50% | 67.032988 | 67.682381 | 1.000000 |
75% | 80.212529 | 79.360605 | 1.000000 |
max | 99.827858 | 98.869436 | 1.000000 |
# sns.set(context="notebook", style="ticks", font_scale=1.5)
# sns.lmplot('exam1', 'exam2', hue='admitted', data=data,
# size=6,
# fit_reg=False,
# scatter_kws={"s": 25}
# )
# plt.plot(x, y, 'grey')
# plt.xlim(0, 130)
# plt.ylim(0, 130)
# plt.title('Decision Boundary')
# plt.show()
fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(data_0["exam1"], data_0["exam2"], color="red", label="0")
ax.scatter(data_1["exam1"], data_1["exam2"],
color="green", alpha=0.5, label="1")
ax.set_title('data')
ax.set_xlabel('exam1')
ax.set_ylabel('exam2')
# ax.grid(True)
plt.plot(x,y,'r', label='Prediction')
plt.legend()
plt.show()
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MphZ4LZp-1626794545982)(D:/学习/计算机/机器学习/吴恩达机器学习/作业代码资料/编程作业-自写/exerise2_own/ML2logistic_regression/output_51_0.png)]
# sns.set(context="notebook", style="ticks", font_scale=1.5)
# sns.lmplot('exam1', 'exam2', hue='admitted', data=data,
# size=6,
# fit_reg=False,
# scatter_kws={"s": 25}
# )
# plt.plot(x, y, 'grey')
# plt.xlim(0, 130)
# plt.ylim(0, 130)
# plt.title('Decision Boundary')
# plt.show()
fig, ax = plt.subplots(figsize=(8, 8))
ax.scatter(data_0["exam1"], data_0["exam2"], color="red", label="0")
ax.scatter(data_1["exam1"], data_1["exam2"],
color="green", alpha=0.5, label="1")
ax.set_title('data')
ax.set_xlabel('exam1')
ax.set_ylabel('exam2')
# ax.grid(True)
plt.plot(x,y,'r', label='Prediction')
plt.legend()
plt.show()
[外链图片转存中…(img-MphZ4LZp-1626794545982)]