数据、代码等相关资料来源于b站日月光华老师视频,此博客作为学习记录。
一、数据集及处理
人力资源数据集:根据各类信息,预测该员工是否会离职。
数据集长这样:
import torch
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = pd.read_csv(r'E:\Code\pytorch\第5章\HR.csv')
print(data.info) # 可查看数据集多少行多少列的大概情况
- 想查看某部分有什么值可使用.unique()方法:
print(data.part.unique())
# 返回data中part部分的单一值,就是看part部分有什么样的值
- 假设现在想知道在哪个part中salary为high的更多,使用.groupby()方法:
print(data.groupby(['salary', 'part']).size())
这种文本的东西是不能直接在深度学习中使用的,要计算首先要经过预处理进行数值化。
使用pd.get_dummies()方法进行数值化:该方法可将数据转化成独热编码(one-hot编码):
# 示例:
print(pd.get_dummies(data.salary))
数值化方法:
# 在数据集后再加三列分别是high、low、medium对应的独热编码
data = data.join(pd.get_dummies(data.salary))
# 删去原有的salary的列
del data['salary']
# 同样的方法应用至part
data = data.join(pd.get_dummies(data.part))
del data['part']
此时,数据集中的所有数据不再包含字符,全为数值。这个数据集是看员工会不会离职,所以left这一列是结果。可查看:
# 对left中的值进行计数
print(data.left.value_counts())
Y_data = data.left.values.reshape(-1, 1) # 转为1维
Y = torch.from_numpy(Y_data).type(torch.float32) # 转换成tensor
# 取出除了left这一列的其他列作为X_data,data.values是转成ndarrary
X_data = data[[c for c in data.columns if c != 'left']].values
X = torch.from_numpy(X_data).type(torch.float32)
数据预处理完毕。
无注释完整预处理代码:
data = pd.read_csv(r'E:\Code\pytorch\第5章\HR.csv')
data = data.join(pd.get_dummies(data.salary))
del data['salary']
data = data.join(pd.get_dummies(data.part))
del data['part']
Y_data = data.left.values.reshape(-1, 1)
Y = torch.from_numpy(Y_data).type(torch.float32)
X_data = data[[c for c in data.columns if c != 'left']].values
X = torch.from_numpy(X_data).type(torch.float32)
二、模型的建立和训练
接下来是对模型的建立:
# nn.Module:继承这个类
# __init__:初始化所有层
# forward:定义模型的运算过程
class Model(nn.Module):
def __init__(self):
super().__init__() # 继承父类的所有属性
self.linear_1 = nn.Linear(20, 64) # 输入20列的特征(数据集决定),输出为64(假设给64个隐藏层)
self.linear_2 = nn.Linear(64, 64) # 接上层64输入,输出64维
self.linear_3 = nn.Linear(64, 1) # 因为是逻辑回归,只要一个输出
self.relu = nn.ReLU()
self.sigmoid = nn.Sigmoid()
def forward(self, input):
x = self.linear_1(input)
x = self.relu(x)
x = self.linear_2(x)
x = self.relu(x)
x = self.linear_3(x)
x = self.sigmoid(x)
return x
训练:
lr = 0.0001
# 每次运行得到模型和其优化方法
def get_model():
model = Model()
opt = torch.optim.Adam(model.parameters(), lr=lr)
return model, opt
model, optim = get_model()
# 定义loss
loss_fn = nn.BCELoss()
batch = 64
num_of_batches = len(data)//batch
epochs = 100
# 开始训练
for epoch in range(epochs):
for i in range(num_of_batches):
start = i*batch
end = start + batch
x = X[start: end]
y = Y[start: end]
y_pred = model(x)
loss = loss_fn(y_pred, y)
optim.zero_grad()
loss.backward()
optim.step()
# 打印出每个epoch的loss情况
with torch.no_grad():
print('epoch:', epoch,'loss:', loss_fn(model(X), Y).data.item())
略……
可看到随着epoch数增加,loss逐渐从0.68左右降到了0.55
三、改进
3.1 dataset方法
对于每次对数据切片不方便,使用dataset进行改进。
# 使用dataset类改写数据输入
# 之前的方法中使用start:end这样的切片形式不方便,故使用dataset进行重构
from torch.utils.data import TensorDataset # 改动
HRdataset = TensorDataset(X,Y) # 改动
model, optim = get_model() # get_model
# 重新写训练部分
for epoch in range(epochs):
for i in range(num_of_batches):
x, y = HRdataset[i*batch:i*batch+batch] #改进的地方
y_pred = model(x)
loss = loss_fn(y_pred, y)
optim.zero_grad()
loss.backward()
optim.step()
# 打印出每个epoch的loss情况
with torch.no_grad():
print('epoch:', epoch,'loss:', loss_fn(model(X), Y).data.item())
这样的情况下就避免了两次对x、y分别进行切片的繁琐了。(可以直接运行啦)
完整代码如下:
import torch
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
from torch import nn
data = pd.read_csv(r'E:\Code\pytorch\第5章\HR.csv')
data = data.join(pd.get_dummies(data.salary))
del data['salary']
data = data.join(pd.get_dummies(data.part))
del data['part']
Y_data = data.left.values.reshape(-1, 1) # 转为1维
Y = torch.from_numpy(Y_data).type(torch.float32) # 转换成tensor
X_data = data[[c for c in data.columns if c != 'left']].values
X = torch.from_numpy(X_data).type(torch.float32)
class Model(nn.Module):
def __init__(self):
super().__init__()
self.linear_1 = nn.Linear(20, 64)
self.linear_2 = nn.Linear(64, 64)
self.linear_3 = nn.Linear(64, 1)
self.relu = nn.ReLU()
self.sigmoid = nn.Sigmoid()
def forward(self, input):
x = self.linear_1(input)
x = self.relu(x)
x = self.linear_2(x)
x = self.relu(x)
x = self.linear_3(x)
x = self.sigmoid(x)
return x
lr = 0.0001
# 每次运行得到模型和其优化方法
def get_model():
model = Model()
opt = torch.optim.Adam(model.parameters(), lr=lr)
return model, opt
# 定义loss
loss_fn = nn.BCELoss()
batch = 64
num_of_batches = len(data)//batch
epochs = 100
from torch.utils.data import TensorDataset
HRdataset = TensorDataset(X,Y)
model, optim = get_model()
for epoch in range(epochs):
for i in range(num_of_batches):
x, y = HRdataset[i*batch:i*batch+batch]
y_pred = model(x)
loss = loss_fn(y_pred, y)
optim.zero_grad()
loss.backward()
optim.step()
# 打印出每个epoch的loss情况
with torch.no_grad():
print('epoch:', epoch,'loss:', loss_fn(model(X), Y).data.item())
3.2 dataloader方法
那么又有新的问题了,这样自仍然是i*batch+batch是以固定顺序对数据进行读取的,如果不想按顺序进行运算该怎么办呢?
dataloader类:创建可以迭代的数据装载器,方便管理批次
3.1部分的dataset是dataloader的第一个参数;
batchsize是他的另一个参数,可以每次训练提供这样一个batch的数据;
shuffle也是他的一个参数,其值为True或False,看是否打乱顺序进行数据的输入;
重写训练部分代码:
from torch.utils.data import TensorDataset
from torch.utils.data import DataLoader
HR_ds = TensorDataset(X,Y)
HR_dl = DataLoader(HR_ds, batch_size=batch, shuffle=True)
model, optim = get_model()
for epoch in range(epochs):
for x, y in HR_dl: # 这样每次提供/返回一个batchsize的x和y
y_pred = model(x)
loss = loss_fn(y_pred, y)
optim.zero_grad()
loss.backward()
optim.step()
with torch.no_grad():
print('epoch:', epoch,'loss:', loss_fn(model(X), Y).data.item())
这样就更加简洁了。