作业2-年收入判断

项目描述

二元分类是机器学习中最基础的问题之一,在这份教学中,你将学会如何实作一个线性二元分类器,来根据人们的个人资料,判断其年收入是否高于 50,000 美元。我们将以两种方法: logistic regression 与 generative model,来达成以上目的,你可以尝试了解、分析两者的设计理念及差别。
实现二分类任务:

  • 个人收入是否超过50000元?

数据集介绍

这个资料集是由UCI Machine Learning Repository 的Census-Income (KDD) Data Set 经过一些处理而得来。为了方便训练,我们移除了一些不必要的资讯,并且稍微平衡了正负两种标记的比例。事实上在训练过程中,只有 X_train、Y_train 和 X_test 这三个经过处理的档案会被使用到,train.csv 和 test.csv 这两个原始资料档则可以提供你一些额外的资讯。

  • 已经去除不必要的属性。
  • 已经平衡正标和负标数据之间的比例。

特征格式

  1. train.csv,test_no_label.csv。
  • 基于文本的原始数据
  • 去掉不必要的属性,平衡正负比例。
  1. X_train, Y_train, X_test(测试)
  • train.csv中的离散特征=>在X_train中onehot编码(学历、状态…)
  • train.csv中的连续特征 => 在X_train中保持不变(年龄、资本损失…)。
  • X_train, X_test : 每一行包含一个510-dim的特征,代表一个样本。
  • Y_train: label = 0 表示 “<=50K” 、 label = 1 表示 " >50K " 。

项目要求

  1. 请动手编写 gradient descent 实现 logistic regression
  2. 请动手实现概率生成模型。
  3. 单个代码块运行时长应低于五分钟。
  4. 禁止使用任何开源的代码(例如,你在GitHub上找到的决策树的实现)。

数据准备

项目数据保存在:work/data/ 目录下。

参考:https://colab.research.google.com/drive/1JaMKJU7hvnDoUfZjvUKzm9u-JLeX6B2C#scrollTo=ZEAKhugPkUyH

预处理

环境导入

import numpy as np
import pandas as pd

读取相应的数据

X_train = pd.read_csv('work/data/X_train')
X_train.shape
#(54256, 511)
Y_train = pd.read_csv('work/data/Y_train')
Y_train.shape
# (54256, 2)

数据处理

X_train.head()
idagePrivateSelf-employed-incorporatedState governmentSelf-employed-not incorporatedNot in universeWithout payFederal governmentNever worked...1.2Not in universe.12Yes.3No.32.30.31.3weeks worked in year9495
003310000000...01001005201
116310000000...01001005201
227100001000...0100100001
334300000000...01001005201
445700000000...01001005201

5 rows × 511 columns

对于X_train进行处理,包括去掉无关的列、将缺示值补0,然后进行归一化:

def NormalizeFunc(X):
    X = X.drop('id',axis=1)
    X.fillna(0)
    X_mean = X.mean()
    X_std = X.std()
    return (X - X_mean) / (X_std + 1e-8), X_mean, X_std

X_train, X_mean, X_std = NormalizeFunc(X_train)
X_train.shape
# (54256, 510)

对于Y_train进行处理:

Y_train = Y_train['label'].fillna(0)

因为numpy矩阵计算比较方便,故转换为numpy矩阵:

X_train = X_train.to_numpy()
Y_train = Y_train.to_numpy()

进行训练集和验证集的划分:

ratio = 0.9

train_size = int(len(X_train) * ratio)
X, Y = X_train[:train_size], Y_train[:train_size]
X_val, Y_val = X_train[train_size:], Y_train[train_size:]

X.shape,Y.shape
# ((48830, 510), (48830,))

编写基础函数

创建逻辑回归函数:

def Sigmoid(z):
    # clip()可以限制输出的大小,保证输出值在一定范围内
    return np.clip(1 / (1.0 + np.exp(-z)), 1e-8, 1 - (1e-8))

def Func(X,w,b):
    # matmul()是矩阵乘法
    return Sigmoid(np.matmul(X,w) + b)

def Predict(X,w,b):
    # round()是四舍五入的实现函数,默认值为0,也就是说保留整数
    # astype()是将ndarray类型转换为某一指定类型
    return np.round(Func(X, w, b)).astype(np.int)

创建准确度计算函数:

def Accuracy(Y_pred, Y_label):
    acc = 1 - np.mean(np.abs(Y_pred - Y_label))
    return acc

创建损失函数,虽然训练过程中不会直接用到损失函数,但是也是比较直观的衡量训练效果的函数。

def Cross_entroy(Y_pred,Y_label):
    return -np.dot(Y_label, np.log(Y_pred)) - np.dot((1 - Y_label), np.log(1 - Y_pred))

创建梯度计算函数:

def Gradient(X,Y_label,w,b):
    Y_pred = Func(X, w, b)
    pred_error = Y_label - Y_pred
    w_grad = -np.sum(pred_error * X.T, 1)
    b_grad = -np.sum(pred_error)
    return w_grad,b_grad

在运行过程中,不希望模型一段时间内只学习正样本或者负样本,这样训练效果并不会很好,所以需要要将数据进行乱序排列,这个在机器学习中也是比较常用的。

def Shuffle(X,Y):
    # 官方的该功能程序实现比较简单
    # 先生成样本对应的序类
    # 然后使用自带函数进行shuffle
    # 返回时直接把shuffled的数据作为下标进行使用
    randomize = np.arange(len(X))
    np.random.shuffle(randomize)
    return (X[randomize], Y[randomize])

训练过程

这里使用小批次梯度下降的方法来进行训练,这算是神经网络中比较常用的一种训练方法了。

首先初始化模型参数:

w = np.zeros((X.shape[1],))  # X.shape[1] 为每一个样本的特征数目
b = np.zeros((1,))

之后设置训练时的相关参数,这里设置step变量目的是为了随着训练的进行,learning rate的值可以逐渐减少,从而期望更好的找到global minima。仔细想想其实这里Adam也是可以用的,Adam使用的只有损失函数的梯度。

max_iter = 10
batch_size = 8
train_size = X.shape[0]
learning_rate = 0.2

step = 1

按照参考,对于损失函数和准确度进行记录,便于之后画图:

train_loss = []
dev_loss = []
train_acc = []
dev_acc = []

紧接着便是模型的训练过程:

for epoch in range(max_iter):
    X, Y = Shuffle(X,Y)

    # Mini-batch training
    for idx in range(int(np.floor(train_size / batch_size))): 
        X_batch = X[idx*batch_size:(idx+1)*batch_size]
        Y_batch = Y[idx*batch_size:(idx+1)*batch_size]

        w_grad, b_grad = Gradient(X_batch,Y_batch,w,b)

        w = w - learning_rate/np.sqrt(step) * w_grad
        b = b - learning_rate/np.sqrt(step) * b_grad

        step += 1

    y_train_pred = Func(X,w,b)
    Y_train_pred = np.round(y_train_pred)
    train_acc.append(Accuracy(Y_train_pred,Y))
    train_loss.append(Cross_entroy(y_train_pred,Y)/train_size)

    y_val_pred = Func(X_val,w,b)
    Y_val_pred = np.round(y_val_pred)
    dev_acc.append(Accuracy(Y_val_pred,Y_val))
    dev_loss.append(Cross_entroy(y_val_pred,Y_val)/X_val.shape[0])

print('Training loss: {}'.format(train_loss[-1]))
print('Development loss: {}'.format(dev_loss[-1]))
print('Training accuracy: {}'.format(train_acc[-1]))
print('Development accuracy: {}'.format(dev_acc[-1]))

# Training loss: 0.2697334076998331
# Development loss: 0.295299642339157
# Training accuracy: 0.8849477780053245
# Development accuracy: 0.8763361592333211

画出损失函数和准确度变化图像:

import matplotlib.pyplot as plt

# Loss curve
plt.plot(train_loss)
plt.plot(dev_loss)
plt.title('Loss')
plt.legend(['train', 'dev'])
plt.savefig('loss.png')
plt.show()

# Accuracy curve
plt.plot(train_acc)
plt.plot(dev_acc)
plt.title('Accuracy')
plt.legend(['train', 'dev'])
plt.savefig('acc.png')
plt.show()

在这里插入图片描述
在这里插入图片描述

预测测试集数据

首先导入数据:

X_test = pd.read_csv('work/data/X_test')
X_test.head()

之后对数据进行处理:

X_test = X_test.drop('id',axis=1)
X_test = (X_test - X_mean) / (X_std + 1e-8)
X_test = X_test.to_numpy()
predictions = Predict(X_test,w,b)
predictions
# array([0, 0, 0, ..., 1, 0, 0])

将预测结果保存到文件中,这个要记住……常常用,但是总记不住。

with open('work/data/logistic', 'w') as f:
    f.write('id,label\n')
    for i, label in  enumerate(predictions):
rk/data/logistic', 'w') as f:
    f.write('id,label\n')
    for i, label in  enumerate(predictions):
        f.write('{},{}\n'.format(i, label))
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值