机器学习(二):逻辑回归(Logistic Reggression)

机器学习(二)

逻辑回归 Logistic Reggression

逻辑回归是一种分类算法,事实上,逻辑回归利用一个g(z)函数将线性回归的连续值映射成一个帮助我们判断的结果,进而进行分类。

以二分类问题为例,通常我们使用Sigmoid函数作为g(z),其表达式为:$g(z) = \frac{1}{1 + e^{-z}}$

将Sigmoid函数可视化后,得到如下图像:

分析图像可知,g(z)的值在0到1之间,当z>0时,g(z)>0.5;当z=0时;g(z)=0.5;当z<0时,g(z)<0.5。

利用Sigmoid函数,我们可以将预测函数构造成:$h_\theta(x) = g(\theta^Tx) = \frac{1}{1 + e^{-\theta{x}}}$

为方便表示,使:$z = \theta^Tx$

因g(z)取值在[0,1],对于而分类问题,我们假设一个类一(1)和一个类二(0),g(z)的值可以视为x这组特征属于1的后验概率估计,即P(y=1|x;θ)。

由此可得:

$hθ(x) = P(y=1|x;θ) = 1−P(y=0|x;θ)$

$P(y=0|x;θ) + P(y=1|x;θ) = 1$

那么,根据概率,我们就可以简单的对数据进行归类,当g(z)>=0.5时,可以归类为1,当g(z)<0.5时,可以归类为0

损失函数

之前我们利用求均方误差的方式得到线性回归的损失函数,拿2个参数的线性回归为例,我们观察损失函数的图像,得到一个平滑的抛物线,用同样的方法,我们观察逻辑回归的损失函数图像,我们会得到一个局部蜷曲,但整体类似抛物线的曲线,对这个图像使用梯度下降的方式,我们很容易会得到损失函数的局部最小值,因此一般在使用逻辑回归时我们不使用这类损失函数。

由于g(z)的值可以视为后验概率,那么我们可以使用似然函数来评估模型的效果,要增强模型效果,实际就是利用极大似然估计不断更新θ。

$P(y=0|x;θ)$ 可以写成:$P(y|x;\theta) = (h_\theta(x))^y(1-h_\theta(x))^{1-y}$

得到似然函数:$L(\theta)= \prod_{i=1}^mP(y_i|xi;\theta) = \prod_{i=1}^m(h_\theta(x_i))^{y_i}(1-h_\theta(x_i))^{1-y_i}$

取对数似然函数,底数为e:

$l(\theta) = logL(\theta) = \sum_{i=1}^m(y_ilogh_\theta(x_i) + (1 - y_i)log(1-h_\theta(x_i)))$

取平均加负号得:$J(\theta) = - \frac{l(\theta)}{m}$

使用matplotlib可视化得到图像如下:

得到损失函数后,使用梯度下降算法求解最小值,此处省略推导过程,$theta$更新如下,$\alpha$为学习率:$\theta_j:=\theta_j - \alpha\frac{1}{m}\sum_{i = 1}^m(h_\theta(x_i) - y_i)x_i^j$

随机梯度下降

在样本中随机抽取batch size的样本数量,用梯度下降更新参数值,由于不需要每次遍历所有的样本,而是抽取样本中的一小部分,可以在不影响收敛结果的情况下,有效加速收敛速度。

$\text{Repeat until convergence Randomly}$

​ $choose B \in {1, 2, . . . , N}$

​ $\theta_j = \theta_j -\alpha\frac{1}{m}\sum(H(x_i) - yi) * Xi^j$

牛顿法

归一化可以从数据预处理的角度解决梯度下降迭代次数过多的问题,牛顿法用二阶导数(Hessian矩阵)解决迭代次数过多的问题。

J的一阶导数如下:

二阶导数:

正则化

L2正则

即在损失函数后加上参数的惩罚项。

​ $J(θ)=−\frac{1}{m}∑[y(i) log(h_θ(x^{(i)}))+(1−y^{(i)}) log(1−h_θ(x^{(i)}))]+\frac{λ}{2m}\sumθ^2_j$

梯度下降:

​ $θj:=θj(1−\frac{αλ}{m}−\frac{α}{m}\sum(h_θ(x^{(i)})−y^{(i)})x^{(i)}_j$

多分类逻辑回归

这里介绍多分类逻辑回归的两种方法

  1. 将多分类问题看成是多个独立的二分类问题的集合

    实现多分类逻辑回归最基础的方法就是对每个类别实现一个二分类逻辑回归分类器,例如一个K分类问题,对每个类别Ki,我们都有Ki和非Ki两类,属于这Ki的样本标记为1,不属于Ki的样本标记为0,由此我们得到K个针对不同标记的逻辑回归分类器,针对一个测试样本,使用K个分类器进行预测时,我们将得到K个预测值,取输出值最大的那一个作为样本的输出预测值,并标记为这个分类器所属的类别。

    即 $y=argmax(h_i(x)), i= 1,2,3 ……K$

  2. 调整损失函数和激活函数,使其适应多分类问题。Sigmoid显然无法很好的解决多分类问题,这里我们使用一个softmax函数,表达式:$S_i = \frac{e^{V_i}}{\sum_je^{V_{j}}}$

    V为数组,Vi为数组中的第i个元素。softmax函数输出了数组中每个值的权重,其值的范围为[0, 1],因此,我们可以将输入的特征值经过计算后,映射成每种类别的概率,这种方法也称为softmax回归。实际上,softmax回归是逻辑回归在多分类情况下的推广,逻辑回归是k=2的特殊softmax回归。

    通常,在多分类问题中,我们需要对类别进行编码,写成如下形式:

    ​ C1 = [1, 0, 0, 0, 0]

    ​ C2 = [0, 1, 0, 0, 0]

    ​ c3 = [0, 0, 1, 0, 0]

    ​ C4 = [0, 0, 0, 1, 0]

    ​ C5 = [0, 0, 0, 0, 1]

    利用交叉熵计算结果与实际值的距离,由此得到损失函数。

    $H(p) = \sum{p}(i)log(\frac{1}{p(i)})$

    损失函数如下:

Tensorflow实现逻辑回归

梯度下降法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
def logistci_reggression_using_GD():
    train_subset = 10000
    graph = tf.Graph()
    
    with graph.as_default():
        tf_train_dataset = tf.constant(train_dataset[:train_subset, :])
        tf_train_labels = tf.constant(train_labels[:train_subset])
        tf_valid_dataset = tf.constant(valid_dataset)
        tf_test_dataset = tf.constant(test_dataset)
    
        # general weight. (a normal distribution)
        weights = tf.Variable(tf.truncated_normal([image_size * image_size, num_labels]))
        biases = tf.Variable(tf.zeros([num_labels]))
    
        logits = tf.matmul(tf_train_dataset, weights) + biases
    
        # loss Fcuntion = cross entropy between logits and one-hot labels
        loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits))
    
        # learning rate = 0.5
        optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)
    
        # e.g. softmax(logits) = [[0.08,0.08,0.08,0.08,0.08,0.08,0.08,0.08,0.080.28], ...]
        train_prediction = tf.nn.softmax(logits)
        valid_prediction = tf.nn.softmax(tf.matmul(tf_valid_dataset, weights) + biases)
        test_prediction = tf.nn.softmax(tf.matmul(tf_test_dataset, weights) + biases)
    
    # train a logistic regresstion classifier
    num_steps = 801
    with tf.Session(graph=graph) as session:
        tf.global_variables_initializer().run()
        print('Initialized')
        for step in range(num_steps):
            _, l, predictions = session.run([optimizer, loss, train_prediction])
    
            if (step % 100 == 0):
                print('Loss at step %d: %f' % (step, l))
                print('Training accuracy: %.1f%%' % accuracy(predictions, train_labels[:train_subset, :]))
                print('Validation accuracy: %.1f%%' % accuracy(valid_prediction.eval(), valid_labels))
        print('Test accuracy: %.1f%%' % accuracy(test_prediction.eval(), test_labels))

随机梯度下降法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
def logistic_reggression_using_SGD():
    batch_size = 128

    graph = tf.Graph()
    with graph.as_default():
        tf_train_data_set = tf.placeholder(tf.float32, shape=(batch_size, image_size * image_size))
        tf_train_labels = tf.placeholder(tf.float32, shape=(batch_size, num_labels))
        tf_valid_dataset = tf.constant(valid_dataset)
        tf_test_dataset = tf.constant(test_dataset)
    
        weights = tf.Variable(tf.truncated_normal([image_size * image_size, num_labels]))
        biases = tf.Variable(tf.zeros([num_labels]))
    
        logits = tf.matmul(tf_train_data_set, weights) + biases
        loss = tf.reduce_mean(tf.nn.softmax_cross_entropy_with_logits(labels=tf_train_labels, logits=logits))
    
        optimizer = tf.train.GradientDescentOptimizer(0.5).minimize(loss)
    
        train_prediction = tf.nn.softmax(logits)
        valid_prediction = tf.nn.softmax(tf.matmul(tf_valid_dataset, weights) + biases)
        test_prediction = tf.nn.softmax(tf.matmul(tf_test_dataset, weights) + biases)
    
    num_step = 3001
    
    with tf.Session(graph=graph) as session:
        tf.global_variables_initializer().run()
        print('Initialized')
        for step in range(num_step):
            offset = (step * batch_size) % (train_labels.shape[0] - batch_size)
            batch_data = train_dataset[offset:(offset + batch_size), :]
            batch_labels = train_labels[offset:(offset + batch_size), :]
            feed_dict = {tf_train_data_set: batch_data, tf_train_labels: batch_labels}
            _ ,l, predictions = session.run([optimizer, loss, train_prediction], feed_dict=feed_dict)
            if (step % 500 == 0):
                print("Minibatch loss at step %d: %f" % (step, l))
                print("Minibatch accuracy: %.1f%%" % accuracy(predictions, batch_labels))
                print("Validation accuracy: %.1f%%" % accuracy(
                    valid_prediction.eval(), valid_labels))
        print("Test accuracy: %.1f%%" % accuracy(test_prediction.eval(), test_labels))