《西瓜书》小记(三) 线性模型

简介

我们将在此章节用 python 自己实现一遍以下几种模型:

  • 线性回归(linear regression)
  • 对数线性回归(log-linear regression)
  • 对数几率回归(logistic regression,或译作逻辑回归)
  • 线性判别分析(Linear Discriminant Analysis,简称 LDA)

在此之前我们需要一点点 python 的编程基础,会用以下的库:

  • numpy:用于数值计算,包含各种矩阵计算的函数。
  • matplotlib:用于数据可视化,将数据绘制成统计图。我们需要使用这个库来画图。
  • pandas:非必需,用于数据分析,便于处理数据。不过我们用到的数据比较简单,不需要这个库也可以处理样本数据。

线性回归(linear regression)

在中学的时候我们学过线性函数(一次函数),即

我们可以从上式推出对于多元线性函数的一般式,即

其中,x = (x1;x2;...;xn)。若令 k = (k1;k2;...;kn),则

再令 θ = (b;k),x^ = (1;x),则得到 f(x) 的一般式

其中 θx^ 均为 n + 1 维向量。对于每一个 n 维特征的样本,都可以用 n + 1维向量 x^ 表示,再用 f(x) 的一般式计算出其预测值。此时,我们只需要知道 θ 的值就能写出线性回归了。我们知道,线性模型是从样本中训练出来的,所以我们需要用样本集 D 来训练出 θ 的值。

令 m 表示样本集 D 的样本数量,d 表示每个样本的特征数量,则我们可以把样本集 D 表示为一个 m x (d + 1) 大小的矩阵 X,即

令向量 y 表示 D 的标签(label),即

通过第二章的学习,我们知道均方误差是回归任务中最常用的性能度量。而最小化均方误差即能使线性模型 f(x) 获得最好的性能,所以 θ 应是令 f(x) 的均方误差最小化的值,即

θ^ 求导得

令上式为零可得 θ^ 的最优解的闭式(closed-form)解,当 XTX满秩矩阵(full-rank matrix)正定矩阵(positive definite matrix)时,令上式为零可得

即得到 θ 的最优解。此方法被称为最小二乘法(least square method),即基于均方误差最小化来进行模型求解的方法。

最终得到线性回归模型:

线性回归(Linear Regression)

以下为线性回归的 python 实现:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

print('正在读取数据...')

data = pd.read_csv('data_regression.csv')

TRAIN_NUM = len(data)
TRAIN_DIM = data.ndim - 1
train_data = data.values[0:TRAIN_NUM, 0:TRAIN_DIM]
train_label = data.values[0:TRAIN_NUM, TRAIN_DIM]

# 处理训练集数据
# TRAIN_NUM   : m
# TRAIN_DIM   : n
# train_data  : m x n
# train_label : 1 x m

print('数据读取完成,总共 ', TRAIN_NUM, ' 行数据')

def linear(data_x, data_y):
    # data_x : train_data
    # data_y : train_label
    # x      : [1] + train_data, m x n
    # y      : train_label.T, m x 1
    m = len(data_y)
    n = np.size(data_x, 1) + 1
    x = np.mat([[1] + list(data_x[i]) for i in range(m)])
    y = np.mat(data_y).T
    theta = (x.T * x).I * x.T * y
    return theta

print('正在计算 Theta 值...')

theta = linear(train_data, train_label)

print('计算 Theta 值成功')
print('Theta = ', theta)

n_grids = 100
x_axis = np.linspace(1, 100, n_grids)
y_axis = theta.T * np.mat([[1] + [x_axis[i]] for i in range(n_grids)]).T
plt.figure('Linear Regression')
plt.plot(x_axis, y_axis.tolist()[0], 'k')
plt.scatter(train_data[0:TRAIN_NUM, 0:TRAIN_DIM], train_label)
plt.show()

数据文件 data_regression.csv

data,label
58,187.8
2.7,26.72
99.4,322.84
10.1,68.36
22.7,118.72
39.9,101.64
97.4,399.64
51,226.6
14.6,25.56
25.7,65.52
27,84.2
26.5,144.4
11.8,21.48
11.1,34.96
69,207.4
14,19.4
69.4,283.84
35.6,93.16
14.8,19.28
51.7,179.12

另外,将代码中计算的 theta 一行

theta = (x.T * x).I * x.T * y

改为

theta = (x.T * x).I * x.T * np.log(y)

再将计算图表 Y 轴的 y_axis 一行

y_axis = theta.T * np.mat([[1] + [x_axis[i]] for i in range(n_grids)]).T

改为

y_axis = np.exp(theta.T * np.mat([[1] + [x_axis[i]] for i in range(n_grids)]).T)

则将线性回归变为了对数线性回归(log-linear regression),即

对数线性回归(Log-Linear Regression)

更一般地,考虑单调可微函数 g(·),令

我们可以得到广义线性模型(generalized linear model),其中函数 g(·) 称为联系函数(link function)

显然,上面的对数线性回归就是在 g(·) = ln(·)时的特例。

对数几率回归(logistic regression,或逻辑回归)

对数几率回归与线性回归相似,主要区别在于对数几率回归使用了 Sigmoid 函数,即形似 S 的函数,如

将线性回归的一般式代入 z,可得

其中,y ∈ (0,1)。

对于二分类任务,可以令函数 y 代表样本被分类为正例的概率,令函数 1 - y 代表样本被分类为反例的概率,则

其中 p(y = i | x)表示数据被分类为 i 的概率。二分类任务中,1 表示正例,0 表示反例。此时,我们就可以通过极大似然法(maximum likelihood method)来估计 θ 值,则有

要使 θ 得到最优解,我们就要最大化 ℓ(θ),即最小化 -ℓ(θ),则有

要使 θ 达到最优解,可用梯度下降法(gradient descent method)牛顿法(Newton method)等。我们用牛顿法来求其最优解,其第 t + 1 轮的更新公式为

其中,关于 θ 的一阶导数、二阶导数分别为

注意在矩阵运算中的 p(y = 1 | x)是 m x 1 的矩阵,而在级数中的 p(y = i | x)是数(即 1 x 1 的矩阵)

最终得到对数几率回归模型:

对数几率回归(Logistic Regression)

以下为对数几率回归的 python 实现:

import numpy as np
import pandas as pd
import matplotlib.pyplot as plt

print('正在读取数据...')

data = pd.read_csv('data_classification.csv')

TRAIN_NUM = len(data)
TRAIN_DIM = data.ndim - 1
train_data = data.values[0:TRAIN_NUM, 0:TRAIN_DIM]
train_label = data.values[0:TRAIN_NUM, TRAIN_DIM]

# 处理训练集数据
# TRAIN_NUM   : m
# TRAIN_DIM   : n
# train_data  : m x n
# train_label : 1 x m

print('数据读取完成,总共', TRAIN_NUM, '行数据')

def p1(x, theta):
    # x     : m x n
    # theta : n x 1
    return 1 - 1 / (1 + np.exp(x * theta))

def p1_single(x, theta):
    # x     : 1 x n
    # theta : n x 1
    return 1 - 1 / (1 + np.exp(theta.T * x.T))

def newtonsMethod(x, y, theta):
    # x     : m x n
    # y     : m x 1
    # theta : n x 1
    m = len(y)
    n = np.size(x, 1)
    fir_d = (1 / m) * x.T * (p1(x, theta) - y)
    sec_d = 0
    for i in range(m):
        sec_d += x[i].T * x[i] * float(p1_single(x[i], theta) * (1 - p1_single(x[i], theta)))
    sec_d /= m
    return theta - sec_d.I * fir_d

def costFunction(x, y, theta):
    # x     : m x n
    # y     : m x 1
    # theta : n x 1
    m = len(y)
    return -1 / m * (y.T * np.log(p1(x, theta)) + (1 - y).T * np.log(1 - p1(x, theta)))

def logistic(data_x, data_y, max_times):
    # data_x    : train_data
    # data_y    : train_label
    # max_times : int
    # x         : [1] + train_data, m x n
    # y         : train_label.T, m x 1
    m = len(data_y)
    n = np.size(data_x, 1) + 1
    x = np.mat([[1] + list(data_x[i]) for i in range(m)])
    y = np.mat(data_y).T
    theta = np.zeros((n, 1))
    times = 0
    J = costFunction(x, y, theta)
    while True:
        last_J = J
        theta = newtonsMethod(x, y, theta)
        J = costFunction(x, y, theta)
        times += 1
        if (last_J - J < 1e-6) or times >= max_times:
            break
    print('总共迭代', times, '次')
    return theta

print('正在计算 Theta 值...')

theta = logistic(train_data, train_label, 20)

print('计算 Theta 值成功')
print('Theta = ', theta)

n_grids = 100
x_axis = np.linspace(0, 100, n_grids)
y_axis = p1(np.mat([[1] + [x_axis[i]] for i in range(n_grids)]), theta)
plt.figure('Linear Regression')
plt.plot(x_axis, y_axis, 'k')
plt.scatter(train_data[0:TRAIN_NUM, 0:TRAIN_DIM], train_label)

y_label = (p1(np.mat([[1] + [train_data[0:TRAIN_NUM, 0:TRAIN_DIM][i]] for i in range(TRAIN_NUM)]), theta) > 0.5).T * 1
correct = np.sum((abs(y_label[0] - train_label) < 1e-3) * 1)
print('分类正确率为:', correct / TRAIN_NUM * 100, '%')
plt.scatter(train_data[0:TRAIN_NUM, 0:TRAIN_DIM], y_label)

plt.show()

数据文件 data_classification.csv

data,label
92.44,0
82,0
40.47,0
25.29,1
18.71,1
51.88,0
69.92,0
29.58,1
50.5,1
65.59,0
81.42,0
89.93,0
89.08,0
17.11,1
20.75,0
22.51,0
77.43,1
60.48,0
41.47,1
54.86,0

线性判别分析(Linear Discriminant Analysis,简称 LDA)

公式太多,暂时推不过来,有缘再加……

小结

这一章给出了几个基本的线性模型,并且介绍了下二分类学习及多分类学习的方法,优化算法方面着重讲解了最小二乘法牛顿法,却没有细讲很是通用的梯度下降法

最小二乘法是用矩阵计算快速给出 θ 的最优解,不过这种方法对于特征数量很大的样本集来说过于缓慢,几乎没法计算。

牛顿法相较于梯度下降法来说,下降的幅度更大,能用更少的迭代次数给出最优解。不过和最小二乘法相似的是,牛顿法对于特征数量大的样本集来说也过于缓慢,主要是由于在求二阶导数的时候用到了黑塞矩阵(Hessian Matrix)。对于此类问题,选择拟牛顿法(Quasi-Newton Methods)更加合适。

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 203,324评论 5 476
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 85,303评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 150,192评论 0 337
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,555评论 1 273
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,569评论 5 365
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,566评论 1 281
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 37,927评论 3 395
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,583评论 0 257
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 40,827评论 1 297
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,590评论 2 320
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,669评论 1 329
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,365评论 4 318
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 38,941评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,928评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,159评论 1 259
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 42,880评论 2 349
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,399评论 2 342

推荐阅读更多精彩内容