zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    gluoncv faster_rcnn 参数修改
    gluoncv 训练自己的数据集,进行目标检测
    Java面试题及答案2020最新版!
    阿里巴巴Java开发手册泰山版下载
    Java基础之如何取舍Joda与 Java8 日期库
    深入理解Java虚拟机3——垃圾回收
    剖析Java OutOfMemoryError异常
    教你如何理解JAVA的I/O类库
    Java源码解读系列(一):ArrayList
    Java 并发之 Executor 框架
  • 原文地址:https://www.cnblogs.com/HL-space/p/10785225.html
Copyright © 2011-2022 走看看