Logistic回归预测收入----台大李宏毅机器学习作业2(HW2)

一、作业说明

  给定训练集spam_train.csv,要求根据每个ID各种属性值来判断该ID对应角色是Winner还是Losser(收入是否大于50K),这是一个典型的二分类问题。

训练集介绍:

  (1)、CSV文件,大小为4000行X59列;

  (2)、4000行数据对应着4000个角色,ID编号从1到4001;

  (3)、59列数据中, 第一列为角色ID,最后一列为分类结果,即label(0、1两种),中间的57列为角色对应的57种属性值;

  (4)、数据集地址:https://pan.baidu.com/s/1mG7ndtlT4jWYHH9V-Rj_5g, 提取码:hwzf 。

二、思路分析及实现

2.1 思路分析

  这是一个典型的二分类问题,结合课上所学内容,决定采用Logistic回归算法。

  与线性回归用于预测不同,Logistic回归则常用于分类(通常是二分类问题)。Logistic回归实质上就是在普通的线性回归后面加上了一个sigmoid函数,把线性回归预测到的数值压缩成为一个概率,进而实现二分类(关于线性回归模型,可参考上一次作业)。

  在损失函数方面,Logistic回归并没有使用传统的欧式距离来度量误差,而使用了交叉熵(用于衡量两个概率分布之间的相似程度)。

   

2.2 数据预处理

  在机器学习中,数据的预处理是非常重要的一环,能直接影响到模型效果的好坏。本次作业的数据相对简单纯净,在数据预处理方面并不需要花太多精力。

  首先是空值处理(尽管没看到空值,但为了以防万一,还是做一下),所有空值用0填充(也可以用平均值、中位数等,视具体情况而定)。

  接着就是把数据范围尽量scale到同一个数量级上,观察数据后发现,多数数据值为0,非0值也都在1附近,只有倒数第二列和倒数第三列数据值较大,可以将这两列分别除上每列的平均值,把数值范围拉到1附近。

  由于并没有给出这57个属性具体是什么属性,因此无法对数据进行进一步的挖掘应用。

  上述操作完成后,将表格的第2列至58列取出为x(shape为4000X57),将最后一列取出做label y(shape为4000X1)。进一步划分训练集和验证集,分别取x、y中前3500个样本为训练集x_test(shape为3500X57),y_test(shape为3500X1),后500个样本为验证集x_val(shape为500X57),y_val(shape为500X1)。

  数据预处理到此结束。

 1 # 从csv中读取有用的信息
 2 df = pd.read_csv('spam_train.csv')
 3 # 空值填0
 4 df = df.fillna(0)
 5 # (4000, 59)
 6 array = np.array(df)
 7 # (4000, 57)
 8 x = array[:, 1:-1]
 9 # scale
10 x[-1] /= np.mean(x[-1])
11 x[-2] /= np.mean(x[-2])
12 # (4000, )
13 y = array[:, -1]
14 
15 # 划分训练集与验证集
16 x_train, x_val = x[0:3500, :], x[3500:4000, :]
17 y_train, y_val = y[0:3500], y[3500:4000]

2.3 模型建立

2.3.1 线性回归

  先对数据做线性回归,得出每个样本对应的回归值。下式为对第n个样本的回归,回归结果为

1 y_pre = weights.dot(x_val[j, :]) + bias

2.3.2 sigmoid函数压缩回归值

  之后将回归结果送进sigmoid函数,得到概率值。

1 sig = 1 / (1 + np.exp(-y_pre)  

2.3.3 误差反向传播

  接着就到重头戏了。众所周知,不管线性回归还是Logistic回归,其关键和核心就在于通过误差的反向传播来更新参数,进而使模型不断优化。因此,损失函数的确定及对各参数的求导就成了重中之重。在分类问题中,模型一般针对各类别输出一个概率分布,因此常用交叉熵作为损失函数。交叉熵可用于衡量两个概率分布之间的相似、统一程度,两个概率分布越相似、越统一,则交叉熵越小;反之,两概率分布之间差异越大、越混乱,则交叉熵越大。

  下式表示k分类问题的交叉熵,P为label,是一个概率分布,常用one_hot编码。例如针对3分类问题而言,若样本属于第一类,则P为(1,0,0),若属于第二类,则P为(0,1,0),若属于第三类,则为(0,0,1)。即所属的类概率值为1,其他类概率值为0。Q为模型得出的概率分布,可以是(0.1,0.8,0.1)等。

  在实际应用中,为求导方便,常使用以e为底的对数。

  针对本次作业而言,虽然模型只输出了一个概率值p,但由于处理的是二分类问题,因此可以很快求出另一概率值为1-p,即可视为模型输出的概率分布为Q(p,1-p)。将本次的label视为概率分布P(y,1-y),即Winner(label为1)的概率分布为(1,0),分类为Losser(label为0)的概率分布为(0,1)。

  损失函数对权重w求偏导,可得:

  因为:

  所以有:

   同理,损失函数对偏置b求偏导,可得:

 1 # 在所有数据上计算梯度,梯度计算时针对损失函数求导,num为样本数量
 2 for j in range(num):
 3     # 线性函数
 4     y_pre = weights.dot(x_train[j, :]) + bias
 5     # sigmoid函数压缩回归值,求得概率
 6     sig = 1 / (1 + np.exp(-y_pre))
 7     # 对偏置b求梯度
 8     b_g += (-1) * (y_train[j] - sig)
 9     # 对权重w求梯度,2 * reg_rate * weights[k] 为正则项,防止过拟合
10     for k in range(dim):
11         w_g[k] += (-1) * (y_train[j] - sig) * x_train[j, k] + 2 * reg_rate * weights[k] 

2.3.4 参数更新

  求出梯度后,再拿原参数减去梯度与学习率的乘积,即可实现参数的更新。

 1 # num为样本数量
 2 b_g /= num
 3 w_g /= num
 4 
 5 # adagrad
 6 bg2_sum += b_g**2
 7 wg2_sum += w_g**2
 8 
 9 # 更新权重和偏置
10 bias -= learning_rate/bg2_sum**0.5 * b_g
11 weights -= learning_rate/wg2_sum**0.5 * w_g

三、代码分享与结果展示

3.1 源代码

  1 import pandas as pd
  2 import numpy as np
  3 
  4 
  5 # 更新参数,训练模型
  6 def train(x_train, y_train, epoch):
  7     num = x_train.shape[0]
  8     dim = x_train.shape[1]
  9     bias = 0  # 偏置值初始化
 10     weights = np.ones(dim)  # 权重初始化
 11     learning_rate = 1  # 初始学习率
 12     reg_rate = 0.001  # 正则项系数
 13     bg2_sum = 0  # 用于存放偏置值的梯度平方和
 14     wg2_sum = np.zeros(dim)  # 用于存放权重的梯度平方和
 15 
 16     for i in range(epoch):
 17         b_g = 0
 18         w_g = np.zeros(dim)
 19         # 在所有数据上计算梯度,梯度计算时针对损失函数求导
 20         for j in range(num):
 21             y_pre = weights.dot(x_train[j, :]) + bias
 22             sig = 1 / (1 + np.exp(-y_pre))
 23             b_g += (-1) * (y_train[j] - sig)
 24             for k in range(dim):
 25                 w_g[k] += (-1) * (y_train[j] - sig) * x_train[j, k] + 2 * reg_rate * weights[k]
 26         b_g /= num
 27         w_g /= num
 28 
 29         # adagrad
 30         bg2_sum += b_g ** 2
 31         wg2_sum += w_g ** 2
 32         # 更新权重和偏置
 33         bias -= learning_rate / bg2_sum ** 0.5 * b_g
 34         weights -= learning_rate / wg2_sum ** 0.5 * w_g
 35 
 36         # 每训练100轮,输出一次在训练集上的正确率
 37         # 在计算loss时,由于涉及到log()运算,因此可能出现无穷大,计算并打印出来的loss为nan
 38         # 有兴趣的同学可以把下面涉及到loss运算的注释去掉,观察一波打印出的loss
 39         if i % 3 == 0:
 40             # loss = 0
 41             acc = 0
 42             result = np.zeros(num)
 43             for j in range(num):
 44                 y_pre = weights.dot(x_train[j, :]) + bias
 45                 sig = 1 / (1 + np.exp(-y_pre))
 46                 if sig >= 0.5:
 47                     result[j] = 1
 48                 else:
 49                     result[j] = 0
 50 
 51                 if result[j] == y_train[j]:
 52                     acc += 1.0
 53                 # loss += (-1) * (y_train[j] * np.log(sig) + (1 - y_train[j]) * np.log(1 - sig))
 54             # print('after {} epochs, the loss on train data is:'.format(i), loss / num)
 55             print('after {} epochs, the acc on train data is:'.format(i), acc / num)
 56 
 57     return weights, bias
 58 
 59 
 60 # 验证模型效果
 61 def validate(x_val, y_val, weights, bias):
 62     num = 500
 63     # loss = 0
 64     acc = 0
 65     result = np.zeros(num)
 66     for j in range(num):
 67         y_pre = weights.dot(x_val[j, :]) + bias
 68         sig = 1 / (1 + np.exp(-y_pre))
 69         if sig >= 0.5:
 70             result[j] = 1
 71         else:
 72             result[j] = 0
 73 
 74         if result[j] == y_val[j]:
 75             acc += 1.0
 76         # loss += (-1) * (y_val[j] * np.log(sig) + (1 - y_val[j]) * np.log(1 - sig))
 77     return acc / num
 78 
 79 
 80 def main():
 81     # 从csv中读取有用的信息
 82     df = pd.read_csv('spam_train.csv')
 83     # 空值填0
 84     df = df.fillna(0)
 85     # (4000, 59)
 86     array = np.array(df)
 87     # (4000, 57)
 88     x = array[:, 1:-1]
 89     # scale
 90     x[:, -1] /= np.mean(x[:, -1])
 91     x[:, -2] /= np.mean(x[:, -2])
 92     # (4000, )
 93     y = array[:, -1]
 94 
 95     # 划分训练集与验证集
 96     x_train, x_val = x[0:3500, :], x[3500:4000, :]
 97     y_train, y_val = y[0:3500], y[3500:4000]
 98 
 99     epoch = 30  # 训练轮数
100     # 开始训练
101     w, b = train(x_train, y_train, epoch)
102     # 在验证集上看效果
103     acc = validate(x_val, y_val, w, b)
104     print('The acc on val data is:', acc)
105 
106 
107 if __name__ == '__main__':
108     main()
View Code

3.2 结果展示

      

  可以看出,在训练30轮后,分类正确率能达到94%左右。

参考资料:

  李宏毅老师机器学习课程视频:https://www.bilibili.com/video/av10590361

  李宏毅老师机器学习课程讲义资料:http://speech.ee.ntu.edu.tw/~tlkagk/courses_ML17_2.html

原文地址:https://www.cnblogs.com/HL-space/p/10785225.html