赞
踩
本博客中使用到的完整代码请移步至: 我的github:https://github.com/qingyujean/Magic-NLPer,求赞求星求鼓励~~~
适用问题:多类分类
模型特点:特征条件下类别的条件概率分布;对数线性模型
模型类型:判别模型
损失函数:逻辑斯蒂损失
学习策略:极大似然估计,或者正则化的极大似然估计
学习算法:梯度下降法、拟牛顿法
逻辑斯蒂回归是解决分类
任务的。逻辑斯蒂回归模型属于“对数线性模型”
。
先看看逻辑斯蒂回归与线性回归的联系。首先想到的是线性回归+阈值,可以转化为一个分类任务,以阈值划分区间,落到不同范围则分成不同的类,例如使用“单位阶跃函数”
:
y
=
{
0
,
i
f
z
<
0
0.5
,
i
f
z
=
0
其
中
z
=
X
θ
1
,
i
f
z
>
0
y=
但这样做有2个问题:
考虑在“线性回归原理小结”中提到的“广义线性模型”
,单调可微
的函数
g
(
⋅
)
g(\cdot)
g(⋅)可作为“联系函数”
,能将线性回归模型的预测值与真实label联系起来,如果真实标记是离散的,就就对应了现在讲的分类任务。
“sigmoid”函数拥有非常好的数学性质:
g
(
z
)
=
1
1
+
e
−
z
g(z)=\frac{1}{1+e^{-z}}
g(z)=1+e−z1
手动实现sigmoid函数
def sigmoid(z):
return 1./(1+np.exp(-z))
使用“sigmoid”函数
代替“单位阶跃函数”
,即使用“sigmoid”函数
作为联系函数
g
(
⋅
)
g(\cdot)
g(⋅),并令
z
=
x
θ
z=x\theta
z=xθ,则得到了二元逻辑斯蒂回归模型
的一般形式:
h
θ
(
x
)
=
1
1
+
e
−
x
θ
h_\theta (x)=\frac{1}{1+e^{-x\theta}}
hθ(x)=1+e−xθ1
x作为样本输入,
h
θ
(
x
)
h_\theta (x)
hθ(x)作为模型输出,并将其视作后验概率
p
(
y
=
1
∣
x
,
θ
)
p(y=1|x,\theta)
p(y=1∣x,θ)。
一个事件的“几率”
,表示该事件发生的概率与不发生的概率的比值,即
p
1
−
p
\frac{p}{1-p}
1−pp,而该事件的“对数几率”
为
l
o
g
i
t
(
p
)
=
l
o
g
p
1
−
p
logit(p)=log\frac{p}{1-p}
logit(p)=log1−pp,而在二元逻辑斯蒂回归中,输出
y
=
1
y=1
y=1的对数几率是:
l
o
g
p
(
y
=
1
∣
x
,
θ
)
1
−
p
(
y
=
1
∣
x
,
θ
)
)
=
x
θ
log\frac{p(y=1|x,\theta)}{1-p(y=1|x,\theta))}=x\theta
log1−p(y=1∣x,θ))p(y=1∣x,θ)=xθ
即在逻辑斯蒂回归中,输出
y
=
1
y=1
y=1的对数几率是输入x的线性函数,或者说上式实际上是在用线性回归模型的预测结果去逼近真实label的对数几率,这里也是最能体现“回归”的地方,残留了线性回归的影子。该模型也叫作“对数几率回归”
。
平方损失在逻辑斯蒂回归分类问题中是非凸的,逻辑回归不是连续的,所以这里不再使用平方损失,而是使用极大似然
来推导损失函数。
把
p
(
y
=
1
∣
x
,
θ
)
p(y=1|x,\theta)
p(y=1∣x,θ)和
p
(
y
=
0
∣
x
,
θ
)
p(y=0|x,\theta)
p(y=0∣x,θ)的式子结合起来,写成一个式子:
p
(
y
∣
x
,
θ
)
=
h
θ
(
x
)
y
(
1
−
h
θ
(
x
)
)
1
−
y
p(y|x,\theta)=h_\theta (x)^y (1-h_\theta (x))^{1-y}
p(y∣x,θ)=hθ(x)y(1−hθ(x))1−y
即为一个样本属于其真实label y的概率分布表达式。
似然函数的目标是:令每个样本属于其真实label的概率越大越好,即“极大似然法”
。
似然函数为:
L
(
θ
)
=
∏
i
=
1
m
p
(
y
(
i
)
∣
x
(
i
)
,
θ
)
=
∏
i
=
1
m
h
θ
(
x
(
i
)
)
y
(
i
)
(
1
−
h
θ
(
x
(
i
)
)
)
1
−
y
(
i
)
L(\theta)=\prod_{i=1}^m p(y^{(i)}|x^{(i)},\theta)=\prod_{i=1}^mh_\theta (x^{(i)})^{y^{(i)}} (1-h_\theta (x^{(i)}))^{1-y^{(i)}}
L(θ)=i=1∏mp(y(i)∣x(i),θ)=i=1∏mhθ(x(i))y(i)(1−hθ(x(i)))1−y(i)
令
L
(
θ
)
L(\theta)
L(θ)越大越好,即求
L
(
θ
)
L(\theta)
L(θ)的极大值,作为优化目标,求解对应的参数,即使用“极大似然估计”
。
对似然函数取对数可使得表达式更简洁,得到对数似然表达式,然后再取负,可得到负对数似然表达式,那么求极大值就可转化为求极小值。
J
(
θ
)
=
−
l
n
L
(
θ
)
=
−
∑
i
=
1
m
[
y
(
i
)
l
o
g
(
h
θ
(
x
(
i
)
)
)
+
(
1
−
y
(
i
)
)
l
o
g
(
1
−
h
θ
(
x
(
i
)
)
)
]
J(\theta)=-lnL(\theta)=-\sum\limits_{i=1}^{m}[y^{(i)}log(h_{\theta}(x^{(i)}))+ (1-y^{(i)})log(1-h_{\theta}(x^{(i)}))]
J(θ)=−lnL(θ)=−i=1∑m[y(i)log(hθ(x(i)))+(1−y(i))log(1−hθ(x(i)))]
此时即得到二元逻辑斯蒂回归的损失函数
,问题转化为以负对数似然函数
为目标的最优化问题(求解极小值)。
J
(
θ
)
=
−
[
(
l
o
g
(
h
θ
(
X
)
)
T
y
+
(
l
o
g
(
1
−
h
θ
(
X
)
)
T
(
1
−
y
)
]
J(\theta) = -[(\,log\,(h_\theta(\pmb{X}))^T\pmb{y}+(\,log\,(1-h_\theta(\pmb{X}))^T(1-\pmb{y})]
J(θ)=−[(log(hθ(XXX))Tyyy+(log(1−hθ(XXX))T(1−yyy)]
其中1表示全1向量,
h
θ
(
X
)
=
1
1
+
e
−
X
θ
h_\theta(\pmb{X})=\frac{1}{1+e^{-X\theta}}
hθ(XXX)=1+e−Xθ1。
手动实现损失函数的计算
# 计算损失函数
def compute_loss(theta, X, y):
if np.ndim(theta) == 1:
theta = theta[:, np.newaxis]
z_x = X.dot(theta) # h=θ^T dot X=θ0*x0+θ1*x1
# print(z_x.shape) # (100,3)x(3,1)=>(100,1)
h_x = sigmoid(z_x) # =>(100,1)
m = y.size # 100,用于对loss求平均
J_loss = -1./m * (np.log(h_x).T.dot(y) + np.log(1-h_x).T.dot(1-y)) # =>(1,1)
if np.isnan(J_loss):
return np.inf
return J_loss[0][0]
加载数据:
data = np.loadtxt(data_dir+'data1.txt', delimiter=',')
print('data shape:', data.shape) # (100,3)
print(data[:5])
输出:
data shape: (100, 3)
[[34.62365962 78.02469282 0. ]
[30.28671077 43.89499752 0. ]
[35.84740877 72.90219803 0. ]
[60.18259939 86.3085521 1. ]
[79.03273605 75.34437644 1. ]]
绘制样本数据点的分布:
# 绘制数据点,便于查看数据分布
def plot_data(data, label_pos, label_neg, axes=None):
# 获取正负样本的index
pos = data[:,2] == 1
neg = data[:,2] == 0
if axes is None:
axes = plt.gca()
axes.scatter(data[pos][:,0], data[pos][:,1], marker='+', c='k', s=60, linewidth=2, label=label_pos)
axes.scatter(data[neg][:,0], data[neg][:,1], c='y', s=60, linewidth=2, label=label_neg)
axes.legend()
plot_data(data, 'y=1', 'y=0')
plt.show()
查看一下 θ = [ 0 , 0 , 0 ] \theta=[0,0,0] θ=[0,0,0]时时的loss:
# 把θ0与x0=[1,1,...,1] 与特征x1,x2,..一起组成一个大X
X = np.c_[np.ones(data.shape[0]), data[:,0:2]]
print(X[:5], X.shape) # (100, 3)
y = np.c_[data[:,2]]
print(y[:5], y.shape) # (100,1)
loss = compute_loss(np.array([[0],[0],[0]]), X, y)
print('\nloss:', loss)
输出:
[[ 1. 34.62365962 78.02469282]
[ 1. 30.28671077 43.89499752]
[ 1. 35.84740877 72.90219803]
[ 1. 60.18259939 86.3085521 ]
[ 1. 79.03273605 75.34437644]] (100, 3)
[[0.]
[0.]
[0.]
[1.]
[1.]] (100, 1)
loss: 0.6931471805599453
逻辑斯蒂回归学习常采用梯度下降法
以及拟牛顿法
。这里给出使用梯度下降法
对损失函数的优化过程。
δ
J
(
θ
)
δ
θ
j
=
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
x
j
(
i
)
\frac{\delta J(\theta)}{\delta\theta_{j}} = \sum_{i=1}^{m} ( h_\theta (x^{(i)})-y^{(i)})x^{(i)}_{j}
δθjδJ(θ)=i=1∑m(hθ(x(i))−y(i))xj(i)
其矩阵/向量形式为:
δ
J
(
θ
)
δ
θ
=
X
T
(
h
θ
(
X
)
−
y
)
\frac{\delta J(\theta)}{\delta\theta} = \pmb{X^T}(h_\theta(\pmb{X})-\pmb{y})
δθδJ(θ)=XTXTXT(hθ(XXX)−yyy)
则
θ
\theta
θ的使用梯度下降法的迭代计算公式为:
θ
=
θ
−
α
X
T
(
h
θ
(
X
)
−
y
)
\theta=\theta-\alpha\pmb{X^T}(h_\theta(\pmb{X})-\pmb{y})
θ=θ−αXTXTXT(hθ(XXX)−yyy)
手动实现梯度计算
# 计算梯度
def compute_gradient(theta, X, y):
if np.ndim(theta) == 1:
theta = theta[:, np.newaxis]
# 计算model输出
z_x = X.dot(theta) # θ^T dot X=θ0*x0+θ1*x1
# print(z_x.shape) # (100,3)x(3,1)=>(100,1)
h_x = sigmoid(z_x) # =>(100,1)
m = y.size # 100 对应loss中求平均时的m
# 计算梯度并更新参数 X.T.dot(h_x-y):(3,100)x(100,1)=>(3,1)
grad = 1./m * X.T.dot(h_x-y) # (3,1)
return grad.flatten() # (3,)
initial_theta = np.zeros(X.shape[1]) # (3,)
initial_theta = initial_theta[:, np.newaxis] # (3,1)
print(initial_theta.shape) # (3,1)
loss = compute_loss(initial_theta, X, y)
grad = compute_gradient(initial_theta, X, y)
print('loss:', loss)
print('grad:', grad)
输出:
(3, 1)
loss: 0.6931471805599453
grad: [ -0.1 -12.00921659 -11.26284221]
使用梯度下降求解参数,利用scipy.optimize.minimize()
函数,指定最小化的目标即损失函数的计算,然后是梯度的计算,优化参数theta,输入输出(X, y),即可实现最小化损失函数。
from scipy.optimize import minimize
res = minimize(compute_loss, initial_theta, args=(X,y),
jac=compute_gradient, options={'maxiter':400})
res
输出:
fun: 0.2034977015895099
hess_inv: array([[ 2.85339493e+03, -2.32908823e+01, -2.27416470e+01],
[-2.32908823e+01, 2.04489131e-01, 1.72969525e-01],
[-2.27416470e+01, 1.72969525e-01, 1.96170322e-01]])
jac: array([-2.68557620e-09, 4.36433485e-07, -1.39671757e-06])
message: 'Optimization terminated successfully.'
nfev: 34
nit: 25
njev: 30
status: 0
success: True
x: array([-25.16131634, 0.2062316 , 0.20147143])
# 查看最后优化得到的参数theta的值
final_updated_theta = res.x
print('theta:', final_updated_theta, final_updated_theta.shape) # (3,)
输出:
theta: [-25.16131634 0.2062316 0.20147143] (3,)
根据求解出的参数 θ \theta θ绘制出决策边界:
# 绘制决策边界 # 边界: x1_min, x1_max = X[:,1].min(), X[:,1].max() # 注意X[0]是常数列[1,1,...,1] x2_min, x2_max = X[:,2].min(), X[:,2].max() xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max)) # 生成网格坐标 # print(xx1.shape, xx2.shape) # (50, 50) (50, 50) XX = np.c_[np.ones(xx1.ravel().shape[0]), xx1.ravel(), xx2.ravel()] # print(XX.shape) # (2500, 3) z_x = XX.dot(final_updated_theta) # (2500, 3)x(3,)=>(2500,) h_x = sigmoid(z_x) h_x = h_x.reshape(xx1.shape) # (50,50) # print(h_x.shape) plt.contour(xx1, xx2, h_x, [0.5], linewidths=1, colors='b') # 决策边界 plot_data(data, 'y=1', 'y=0') # # 样本数据点 plt.legend(loc=1)
为防止模型过拟合,一般可以加入正则化项
,常见的有L1正则化
和L2正则化
。例如这里考虑L2正则化,那么其加正则化项的损失函数
代数表达式为:
J
(
θ
)
=
−
[
(
l
o
g
(
h
θ
(
X
)
)
T
y
+
(
l
o
g
(
1
−
h
θ
(
X
)
)
T
(
1
−
y
)
]
+
λ
2
∣
∣
θ
∣
∣
2
2
J(\theta) = -[(\,log\,(h_\theta(\pmb{X}))^T\pmb{y}+(\,log\,(1-h_\theta(\pmb{X}))^T(1-\pmb{y})]+\frac{\lambda}{2}||\theta||^2_2
J(θ)=−[(log(hθ(XXX))Tyyy+(log(1−hθ(XXX))T(1−yyy)]+2λ∣∣θ∣∣22
即
J
(
θ
)
=
−
[
(
l
o
g
(
h
θ
(
X
)
)
T
y
+
(
l
o
g
(
1
−
h
θ
(
X
)
)
T
(
1
−
y
)
]
+
λ
2
∑
j
=
1
n
θ
j
2
即J(\theta)=-[(\,log\,(h_\theta(\pmb{X}))^T\pmb{y}+(\,log\,(1-h_\theta(\pmb{X}))^T(1-\pmb{y})]+\frac{\lambda}{2}\sum_{j=1}^{n}\theta_{j}^{2}
即J(θ)=−[(log(hθ(XXX))Tyyy+(log(1−hθ(XXX))T(1−yyy)]+2λj=1∑nθj2
注意:
正则化项中
θ
\theta
θ从下标1开始计算,
θ
0
\theta_0
θ0不参与计算。
加正则项的逻辑斯蒂回归的损失函数手动实现
# 计算损失函数 def compute_loss_reg(theta, reg, *args): if np.ndim(theta) == 1: theta = theta[:, np.newaxis] # (28,)=>(28,1) XX, y = args z_x = XX.dot(theta) # h=θ^T dot X=θ0*x0+θ1*x1 # print(z_x.shape) # (118,28)x(28,1)=>(118,1) h_x = sigmoid(z_x) # =>(118,1) m = y.size # 118 用于求平均损失 J_loss = -1./m * (np.log(h_x).T.dot(y) + np.log(1-h_x).T.dot(1-y)) # =>(1,1) # 注意:正则项的求和中j是从1开始的,而不是0,因为约束的是特征,而\theta_0对应的是x_0=1常数项 reg_item = reg/(2.*m) * np.sum(np.square(theta[:,0][1:])) J_loss = J_loss + reg_item if np.isnan(J_loss): return np.inf return J_loss[0][0]
那么加正则项后的偏导(梯度)
代数表达式为:
δ
J
(
θ
)
δ
θ
j
=
∑
i
=
1
m
(
h
θ
(
x
(
i
)
)
−
y
(
i
)
)
x
j
(
i
)
+
λ
θ
j
\frac{\delta J(\theta)}{\delta\theta_{j}} = \sum_{i=1}^{m} ( h_\theta (x^{(i)})-y^{(i)})x^{(i)}_{j}+\lambda\theta_j
δθjδJ(θ)=i=1∑m(hθ(x(i))−y(i))xj(i)+λθj
其矩阵/向量形式为:
δ
J
(
θ
)
δ
θ
=
X
T
(
h
θ
(
X
)
−
y
)
+
λ
θ
\frac{\delta J(\theta)}{\delta\theta} = \pmb{X^T}(h_\theta(\pmb{X})-\pmb{y})+\lambda\pmb{\theta}
δθδJ(θ)=XTXTXT(hθ(XXX)−yyy)+λθθθ
加正则项的梯度计算的手动实现
# 计算梯度 def compute_gradient_reg(theta, reg, *args): if np.ndim(theta) == 1: theta = theta[:, np.newaxis] # (28,)=>(28,1) XX, y = args # 计算model输出 z_x = XX.dot(theta) # θ^T dot X=θ0*x0+θ1*x1 # print(z_x.shape) # (118,28)x(28,1)=>(118,1) h_x = sigmoid(z_x) # =>(118,1) m = y.size # 118 对应于平均损失中的m # 计算梯度并更新参数 X.T.dot(h_x-y):(28,118)x(118,1)=>(28,1) grad = 1./m * XX.T.dot(h_x-y) # (28,1) reg_item = reg/m * np.r_[ [[0]], theta[1:] ] # (1,1) 行拼接 (27,1)=>(28,1) grad = grad + reg_item # (28,1) return grad.flatten() # (28,)
加载数据:
data2 = np.loadtxt(data_dir+'data2.txt', delimiter=',')
print('data shape:', data2.shape) # (100,3)
print(data2[:5])
plot_data(data2, 'y=1', 'y=0')
输出:
data shape: (118, 3)
[[ 0.051267 0.69956 1. ]
[-0.092742 0.68494 1. ]
[-0.21371 0.69225 1. ]
[-0.375 0.50219 1. ]
[-0.51325 0.46564 1. ]]
可见该数据不是直接线性可分的,不能直接使用逻辑斯蒂回归。考虑在“线性回归原理小结”中提到“多项式回归”
,这里使用“多项式特征”(sklearn.preprocessing.PolynomialFeatures)
对原始数据
X
X
X进行特征转化,对转化后的数据
X
′
X'
X′再使用逻辑斯蒂回归。由于数据比之前的复杂,这次便可在学习时加入正则项
以避免过拟合
。。
X = data2[:, 0:2] y = np.c_[data2[:,2]] print(X.shape, y.shape) # (118, 2) (118, 1) poly = PolynomialFeatures(6) # 最高次项为6次 XX = poly.fit_transform(X) # X是有2个特征,XX有28个特征(含组合特征) XX.shape # (118, 28) # 0次项:1个,1次项:2个,2次项:3个(x1^2,x2^2,x1x2),3次项:4个 # 4次项:5个,5次项:6个,6次项:7个,一共28个特征 initial_theta = np.zeros(XX.shape[1]) # (28,) initial_theta = initial_theta[:, np.newaxis] print(initial_theta.shape) # (28,1) loss = compute_loss_reg(initial_theta, 1, XX, y) grad = compute_gradient_reg(initial_theta, 1, XX, y) print('loss:', loss) print('grad:', grad)
输出:
(28, 1)
loss: 0.6931471805599454
grad: [8.47457627e-03 1.87880932e-02 7.77711864e-05 5.03446395e-02
1.15013308e-02 3.76648474e-02 1.83559872e-02 7.32393391e-03
8.19244468e-03 2.34764889e-02 3.93486234e-02 2.23923907e-03
1.28600503e-02 3.09593720e-03 3.93028171e-02 1.99707467e-02
4.32983232e-03 3.38643902e-03 5.83822078e-03 4.47629067e-03
3.10079849e-02 3.10312442e-02 1.09740238e-03 6.31570797e-03
4.08503006e-04 7.26504316e-03 1.37646175e-03 3.87936363e-02]
绘制决策边界,并查看不同的正则化项系数,对于决策边界的影响
# 预测 def predict(theta, X, threshold=0.5): z_x = X.dot(theta) # (b,3)x(3,1)=>(b,1) h_x = sigmoid(z_x) # (b,1) pred = h_x >= threshold return pred.astype('int') # (b,1) b个样本的预测结果 fig, axes = plt.subplots(1, 3, sharey=True, figsize=(17,5)) print(axes.shape) # (3,) lambda_list = [0., 1., 100.] for i,C in enumerate(lambda_list): # 最小化损失 res2 = minimize(compute_loss_reg, initial_theta, args=(C,XX,y), jac=compute_gradient_reg, options={'maxiter':3000}) final_updated_theta = res2.x # (28,) # print('theta:', final_updated_theta.shape) # (28,) # 计算准确率 XX:(118, 28) y:(118, 1) theta:=>(28,1) pred = predict(final_updated_theta.reshape(-1,1), XX) # (118,1) # print(pred.shape) # (118,1) accuracy = 100. * sum(pred.ravel() == y.ravel()) / y.size # 绘制原始数据分布 plot_data(data2, 'y=1', 'y=0', axes=axes.flatten()[i]) # 绘制决策边界 X:(118,2) y:(118,1) # 边界: x1_min, x1_max = X[:,0].min(), X[:,0].max() # 注意这里的X不含常数列[1,1,...,1] x2_min, x2_max = X[:,1].min(), X[:,1].max() xx1, xx2 = np.meshgrid(np.linspace(x1_min, x1_max), np.linspace(x2_min, x2_max)) # 生成网格坐标 # print(xx1.shape, xx2.shape) # (50, 50) (50, 50) X_ = np.c_[xx1.ravel(), xx2.ravel()] # print(X_.shape) # (2500,2) # 没算常数列 XX_ = poly.fit_transform(X_) # (2500,28) # print(XX_.shape) # (2500,28) z_x = XX_.dot(final_updated_theta) # (2500, 28)x(28,)=>(2500,) h_x = sigmoid(z_x) # (2500,) h_x = h_x.reshape(xx1.shape) # (50,50) #print(h_x.shape) # (50,50) axes.flatten()[i].contour(xx1, xx2, h_x, [0.5], linewidths=1, colors='g') # 决策边界 # 设置标题 axes.flatten()[i].set_title('Train accuracy {:.2f}% with Lambda = {}'.format(accuracy, C))
二项逻辑斯蒂回归推广到多项逻辑斯蒂回归
,可用于解决多分类问题
。假设离散型随机变量y的取值集合为{1,2,…,K},那么多项逻辑斯蒂回归的概率分布表达式如下:
p
(
y
=
k
∣
x
,
θ
)
=
e
x
θ
k
1
+
∑
k
=
1
K
−
1
e
x
θ
k
,
k
=
1
,
2
,
.
.
.
,
K
−
1
p(y=k|x,\theta ) = \frac{e^{x\theta_k}} {1+\sum\limits_{k=1}^{K-1}e^{x\theta_k}}\;\;,\;\;k=1,2,...,K-1
p(y=k∣x,θ)=1+k=1∑K−1exθkexθk,k=1,2,...,K−1
p
(
y
=
K
∣
x
,
θ
)
=
1
1
+
∑
k
=
1
K
−
1
e
x
θ
k
p(y=K|x,\theta ) = \frac{1} {1+\sum\limits_{k=1}^{K-1}e^{x\theta_k}}
p(y=K∣x,θ)=1+k=1∑K−1exθk1
注意:
这里的“多项逻辑斯蒂回归”
是解决多分类问题,而前面以及“线性回归原理小结”中提到的“多项式回归”
是解决 “非线性特征” 经过特征转化,得到 多元“线性特征” 的问题。
利用二分类器解决多分类问题,关键在于如何对多分类任务进行拆分,以及如何对多个分类器进行集成。常用的拆分策略:
“投票”
,产生最终分类结果。完整代码请移步至: 我的github:https://github.com/qingyujean/Magic-NLPer,求赞求星求鼓励~~~
[1] 统计学习方法(第2版) 李航
[2] 机器学习(西瓜书) 周志华
[3] 逻辑回归原理小结 刘建平
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。