机器学习笔记(一)——逻辑回归

逻辑回归

Hypothesis function 预测函数

h

θ

(

x

)

=

1

1

+

e

x

θ

T

(1)

h_{theta}(x) = frac{1}{1+e^{-bold{x}*theta^T}} tag{1}

hθ(x)=1+exθT1(1)
在这里插入图片描述

代价函数

Cost

(

h

θ

(

x

)

,

y

)

=

{

log

(

h

θ

(

x

)

)

 if 

y

=

1

log

(

1

h

θ

(

x

)

)

 if 

y

=

0

(2)

operatorname{Cost}left(h_{theta}(x), yright)=left{begin{aligned} -log left(h_{theta}(x)right) & text { if } y=1 \ -log left(1-h_{theta}(x)right) & text { if } y=0 end{aligned}right. tag{2}

Cost(hθ(x),y)={log(hθ(x))log(1hθ(x)) if y=1 if y=0(2)

Cost

(

h

θ

(

x

)

,

y

)

=

y

×

log

(

h

θ

(

x

)

)

(

1

y

)

×

log

(

1

h

θ

(

x

)

)

(3)

operatorname{Cost}left(h_{theta}(x), yright)=-y times log left(h_{theta}(x)right)-(1-y) times log left(1-h_{theta}(x)right) tag{3}

Cost(hθ(x),y)=y×log(hθ(x))(1y)×log(1hθ(x))(3)

qquad

其图如下,也就是说当

y

=

1

y=1

y=1时,

h

θ

(

x

)

h_{theta}(x)

hθ(x)越接近于1,

C

o

s

t

Cost

Cost越小。当

y

=

0

y=0

y=0时,

h

θ

(

x

)

h_{theta}(x)

hθ(x)越接近于0,

C

o

s

t

Cost

Cost越小。换句话说,在使用这样的代价函数进行模型的训练时,会让

y

=

0

y=0

y=0时的

h

θ

(

x

)

h_theta(x)

hθ(x)趋于

0

0

0,会让

y

=

1

y=1

y=1时的

h

θ

(

x

)

h_theta(x)

hθ(x)趋于

1

1

1。也就是说,对于最终训练得到的模型,

h

θ

(

x

)

h_theta(x)

hθ(x)越大,表明它被判别为这一类的可能性越大。

在这里插入图片描述

qquad

最终的代价函数定义为如下,其中m代表总的x的样本量。

J

(

θ

)

=

1

m

i

=

1

m

[

y

(

i

)

log

(

h

θ

(

x

(

i

)

)

)

(

1

y

(

i

)

)

log

(

1

h

θ

(

x

(

i

)

)

)

]

(4)

J(theta)=frac{1}{m} sum_{i=1}^{m}left[-y^{(i)} log left(h_{theta}left(x^{(i)}right)right)-left(1-y^{(i)}right) log left(1-h_{theta}left(x^{(i)}right)right)right] tag{4}

J(θ)=m1i=1m[y(i)log(hθ(x(i)))(1y(i))log(1hθ(x(i)))](4)

qquad

J

(

θ

)

J(theta)

J(θ)的偏导数的最终推导结果如下,其中i表示第i个数据,j表示该数据的第j维的值。

θ

j

J

(

θ

)

=

θ

j

[

1

m

i

=

1

m

[

y

(

i

)

log

(

1

+

e

θ

T

x

(

i

)

)

(

1

y

(

i

)

)

log

(

1

+

e

θ

T

x

(

i

)

)

]

]

=

1

m

i

=

1

m

[

h

θ

(

x

(

i

)

)

y

(

i

)

]

x

j

(

i

)

(5)

begin{aligned} frac{partial}{partial theta_{j}} J(theta)=&frac{partial}{partial theta_{j}}left[-frac{1}{m} sum_{i=1}^{m}left[-y^{(i)} log left(1+e^{-theta^{T} x^{(i)}}right)-left(1-y^{(i)}right) log left(1+e^{theta^{T} x^{(i)}}right)right]right]\ =&frac{1}{m} sum_{i=1}^{m}left[h_{theta}left(x^{(i)}right)-y^{(i)}right] x_{j}^{(i)} end{aligned} tag{5}

θjJ(θ)==θj[m1i=1m[y(i)log(1+eθTx(i))(1y(i))log(1+eθTx(i))]]m1i=1m[hθ(x(i))y(i)]xj(i)(5)

qquad

对于正则化后的逻辑回归模型,定义公式如下。其中m仍然表示样本的总数,n表示

θ

theta

θ的维度,需要注意的是

θ

bold{theta}

θ

θ

1

theta_1

θ1开始进行正则化。

J

(

θ

)

=

1

m

i

=

1

m

[

y

(

i

)

log

(

h

θ

(

x

(

i

)

)

)

(

1

y

(

i

)

)

log

(

1

h

θ

(

x

(

i

)

)

)

]

+

λ

2

m

j

=

1

n

θ

j

2

(6)

J(theta)=frac{1}{m} sum_{i=1}^{m}left[-y^{(i)} log left(h_{theta}left(x^{(i)}right)right)-left(1-y^{(i)}right) log left(1-h_{theta}left(x^{(i)}right)right)right]+frac{lambda}{2 m} sum_{j=1}^{n} theta_{j}^{2} tag{6}

J(θ)=m1i=1m[y(i)log(hθ(x(i)))(1y(i))log(1hθ(x(i)))]+2mλj=1nθj2(6)

梯度下降算法

qquad

在编写代码时,提倡使用向量化的方法,将N个参数同时进行更新。要最小化代价函数,通过求导,得出的梯度下降算法如下,其中的i,j的定义与(6)式相同。

R

e

p

e

a

t

u

n

t

i

l

c

o

n

v

e

r

g

e

n

c

e

{

θ

0

:

=

θ

0

α

1

m

i

=

1

m

(

(

h

θ

(

x

(

i

)

)

y

(

i

)

)

x

0

(

i

)

)

θ

j

:

=

θ

j

α

1

m

i

=

1

m

(

(

h

θ

(

x

(

i

)

)

y

(

i

)

)

x

j

(

i

)

+

λ

m

θ

j

)

 for 

j

=

1

,

2

,

n

}

(7)

begin{aligned} Repeat , until , convergence,{ &theta_{0}:=theta_{0}-alpha frac{1}{m} sum_{i=1}^{m}left(left(h_{theta}left(x^{(i)}right)-y^{(i)}right) cdot x_{0}^{(i)}right) \ &theta_{j}:=theta_{j}-alpha frac{1}{m} sum_{i=1}^{m}left(left(h_{theta}left(x^{(i)}right)-y^{(i)}right) cdot x_{j}^{(i)}+frac{lambda}{m} theta_{j}right) \ & text { for } j=1,2, ldots n \ &} end{aligned} tag{7}

Repeatuntilconvergence{θ0:=θ0αm1i=1m((hθ(x(i))y(i))x0(i))θj:=θjαm1i=1m((hθ(x(i))y(i))xj(i)+mλθj) for j=1,2,n}(7)

逻辑回归用于多分类问题

qquad

对于逻辑回归用于多分类问题时,由于逻辑回归只能一次在2个类之间进行分类,因此我们需要多类分类的策略。每个分类器在“类别

i

i

i”和“不是

i

i

i”之间决定。我们将把分类器训练包含在一个函数中,该函数计算

N

N

N个分类器中的每个分类器的最终权重,得到最后的结果。

qquad

例如:对于一个

N

N

N分类问题来说,我们要制定并训练出

N

N

N个分类器。也就是说,我们需要先将原样本的y标签变化为N份针对不同分类器的y标签。对于从

i

=

1

,

,

N

i=1,dots,N

i=1,,N类的y,我们都以

i

i

i为主体转化出N份的y标签,值只有1和0,他们分别对应是第

i

i

i类和不是第

i

i

i类。

qquad

训练完成这

N

N

N个分类器,得到不同分类器下的

θ

theta

θ值,并加他们都添加到

a

l

l

_

t

h

e

t

a

all_theta

all_theta当中。之后,我们就可以进行逻辑

N

N

N类回归预测。对于输入的样本值,我们要在

N

N

N个分类器中进行预测,最终最高

h

θ

(

x

)

h_theta(x)

hθ(x)值对应的那一类就是我们最终预测的结果。

代码实例

qquad

接下来,我将用逻辑回归来解决经典的MNIST手写数字识别问题,下面是它详细的代码介绍。

qquad

首先载入数据集:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
from scipy.io import loadmat
import numpy as np

# 逻辑回归做多类别预测
data = loadmat('ex3data1.mat')
print(data['X'].shape,data['y'].shape)

qquad

定义sigmoid函数(

h

θ

h_theta

hθ函数)和

C

o

s

t

Cost

Cost函数:

#sigmoid 函数
def sigmoid(z):
    return 1 / (1 + np.exp(-z))

#代价函数
def cost(theta, X, y, learningRate):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)
    #sigmoid函数里套 X * theta.T 就是 H函数   (5000,400) * (400,1)
    #有关*的注释:
    #a*b 如果a和b都是narray 且 规格相同,按普通乘法来,如果他们都是matrix,必须按矩阵乘法的规格来
    #因此这里X是(5000,400) theta 则是(400,1) 相乘之后是 (5000,1)
    #y标签里的值是1-10 10代表0
    #np.multiply 两矩阵规格相同时,计算得到其内积
    first = np.multiply(-y, np.log(sigmoid(X * theta.T)))
    second = np.multiply((1 - y), np.log(1 - sigmoid(X * theta.T)))
    #len(X) 5000-也就是X的第一维的长度 这里表示5000 theta 正则化前就是乘以1/2m
    #theta[:,1:theta.shape[1]]表示从theta1开始正则化, theta0不参与正则化
    reg = (learningRate / (2 * len(X))) * np.sum(np.power(theta[:,1:theta.shape[1]], 2))
    return np.sum(first - second) / len(X) + reg    #first - second 后要求sum

qquad

举例非向量化用for循环求梯度:

def gradient_with_loop(theta, X, y, learningRate):
    theta = np.matrix(theta)
    #X 本身是(400,)的一个向量,我们要把它转为列向量(转换为矩阵,它就变成了(1,400)的列向量)
    # 把一维的向量转换为列向量--->意味着把它转换为矩阵
    X = np.matrix(X)
    y = np.matrix(y)

    #取[1] 也就是400
    parameters = int(theta.ravel().shape[1])
    grad = np.zeros(parameters)

    #(5000,400) * (400,1) ->(5000,1)
    error = sigmoid(X * theta.T) - y

    for i in range(parameters):
        # 在error外面乘x_j^i i是代表第几个X 因为这里是 np.multiply 
        # 这里 就是将上一步得到的error 每行和 X[,i]的每行相乘
        # 与公式是一致的 至于这个代码里的i是指theta 对应的i 也就是公式中的j 因为x是有400维的
        # 公式当中x^(i)_{j} i 是指第几个数据 他都是400维  j是指400中的具体第几个维度
        # 所以对于不同的theta_{j} 它是对应不同维度(400)的x
        # 这里的error (5000,1) X[:,i] 也是(5000,1) 它的每一行 
        # 5000行的每个值都是乘了5000个X的第i个 (5000,i) i代表theta的下标
        term = np.multiply(error, X[:, i])

        if (i == 0):
            grad[i] = np.sum(term) / len(X)
        else:
            grad[i] = (np.sum(term) / len(X)) + ((learningRate / len(X)) * theta[:, i])

    return grad

qquad

举例向量化的梯度函数:

qquad

梯度函数是计算

θ

theta

θ的梯度,它的大小与

θ

theta

θ的大小是一样的(X的维度)。在进行梯度下降的时候是要把所有变量都要走一遍 所以要sum所有/m。我们看到动态的梯度/参数在变化是因为有大量的样本,一个样本是过一遍,而不是因为epoch的变化(不要混淆)。

#向量化的梯度函数
def gradient(theta, X, y, learningRate):
    theta = np.matrix(theta)
    X = np.matrix(X)
    y = np.matrix(y)

    parameters = int(theta.ravel().shape[1])
    # X * theta.T ->(5000,1) x^(i) 第 i个数据与 theta向量相乘 theta.T是(400,1) theta原来是(1,400)
    error = sigmoid(X * theta.T) - y

    #(400,5000) * (5000,1) -> (400,1) 第n个x 的error 乘以 第n个 x 的x_j^{i} 并加和 (1,400)
    #X.T * error X是行向量,(400,5000) * (5000,1) -> (400,1) 转置 (1,400)
    #grad也是(1,400)--->每个theta的gradient嘛
    grad = ((X.T * error) / len(X)).T + ((learningRate / len(X)) * theta)

    #在进行梯度下降的时候是要把所有变量都要走一遍 所以要sum所有的/m

    # intercept gradient is not regularized
    # 特殊处理一下 拦截 (0,0) 也就是theta 0
    grad[0, 0] = np.sum(np.multiply(error, X[:, 0])) / len(X)

    return np.array(grad).ravel()

qquad

现在我们已经定义了代价函数和梯度函数,现在是构建分类器的时候了。对于这个任务,我们有

10

10

10个可能的类,并且由于逻辑回归只能一次在

2

2

2个类之间进行分类,我们需要多类分类的策略。在本练习中,我们的任务是实现一对一全分类方法,其中具有k个不同类的标签就有k个分类器,每个分类器在“类别

i

i

i”和“不是

i

i

i”之间决定。

qquad

我们将把分类器训练包含在一个函数中,该函数计算

10

10

10个分类器中的每个分类器的最终权重,并将权重返回为

k

(

n

+

1

)

k*(n+1)

k(n+1)数组,其中n是参数数量。 权重返回是

k

(

n

+

1

)

k*(n+1)

k(n+1)规格。

qquad

计算all_theta,

10

10

10个分类器的

θ

theta

θ的结果如下:

qquad

注意,minimize函数输入的参数,cost代价函数(要变小)和gradient(更新规则)都需要。

from scipy.optimize import minimize

def one_vs_all(X, y, num_labels, learning_rate):
    rows = X.shape[0]
    params = X.shape[1]

    # k X (n + 1) array for the parameters of each of the k classifiers
    # k个分类器 有k*(n+1)个权重
    all_theta = np.zeros((num_labels, params + 1))

    # insert a column of ones at the beginning for the intercept term
    # 给X在最前面插入了一个 1 与 theta_0相加->代表截距/常数项
    X = np.insert(X, 0, values=np.ones(rows), axis=1)

    # labels are 1-indexed instead of 0-indexed
    # 1-10 对于5000个y里 如果它==i,就变1,!=i就变0
    for i in range(1, num_labels + 1):
        theta = np.zeros(params + 1)
        # 意思就是:对于5000个不同类别的y 类别变成 第 i类和不是第i类
        y_i = np.array([1 if label == i else 0 for label in y])
        y_i = np.reshape(y_i, (rows, 1))

        # minimize the objective function
        fmin = minimize(fun=cost, x0=theta, args=(X, y_i, learning_rate), method='TNC', jac=gradient)
        all_theta[i - 1, :] = fmin.x
        # 第 i 种分类的 (n+1)个theta值

    return all_theta

qquad

定义预测函数,对于这一步,我们将计算每个类的类概率,对于每个训练样本(使用当然的向量化代码),并将输出类标签为具有最高概率的类。

def predict_all(X, all_theta):
    rows = X.shape[0]
    params = X.shape[1]
    num_labels = all_theta.shape[0]

    # same as before, insert ones to match the shape
    X = np.insert(X, 0, values=np.ones(rows), axis=1)

    # convert to matrices
    X = np.matrix(X)
    all_theta = np.matrix(all_theta)

    # compute the class probability for each class on each training instance
    h = sigmoid(X * all_theta.T)
    # 对于第i类来说,判断是第i类或者不是第i类,h(x)值越大,说明是第i类的可能性越大

    # create array of the index with the maximum probability
    h_argmax = np.argmax(h, axis=1)

    # because our array was zero-indexed we need to add one for the true label prediction
    h_argmax = h_argmax + 1

    return h_argmax

qquad

进行预测。这里需要注意的几点:首先,我们为

θ

theta

θ添加了一个额外的参数(与训练数据一列),以计算截距项(常数项)。其次,我们将y从类标签转换为每个分类器的二进制值(要么是

i

i

i类,要么不是

i

i

i类)。最后,我们使用SciPy的较新优化API来最小化每个分类器的代价函数。 如果指定的话,API将采用目标函数,初始参数集,优化方法和jacobian(渐变)函数。然后将优化程序找到的参数分配给参数数组。

qquad

实现向量化代码的一个更具挑战性的部分是正确地写入所有的矩阵,保证维度正确。注意,

θ

theta

θ是一维数组,因此当它被转换为计算梯度的代码中的矩阵时,它变为

(

1

×

401

)

(1×401)

(1×401)矩阵。我们还检查y中的类标签,以确保它们看起来与想象的一致。

all_theta = one_vs_all(data['X'], data['y'], 10, 1)
y_pred = predict_all(data['X'], all_theta)
correct = [1 if a == b else 0 for (a, b) in zip(y_pred, data['y'])]
accuracy = (sum(map(int, correct)) / float(len(correct)))
print ('accuracy = {0}%'.format(accuracy * 100))

qquad

最终,使用逻辑回归完成MNIST分类达到的准确度为:94.46%。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
THE END
分享
二维码
< <上一篇
下一篇>>