模型改写,多层感知机训练,dataset改写数据,划分数据集验证集,添加正确率
数据处理
读入数据
import torch
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
data = pd.read_csv('./HR.csv')
data.head()
data.info()
数据字符类型处理
然后我们发现data中的part和salary是object类型,所以我们将这两个打印看看都有那些类型
data.part.unique()
# array(['sales', 'accounting', 'hr', 'technical', 'support', 'management',
# 'IT', 'product_mng', 'marketing', 'RandD'], dtype=object)
data.salary.unique()
# array(['low', 'medium', 'high'], dtype=object)
然后我们将这两个类型转化成one-hot编码,我们用pd.get_dummies()方法。
pandas.get_dummies(data, prefix=None, prefix_sep='_', dummy_na=False, columns=None, sparse=False, drop_first=False, dtype=None)
prefix:str, list of str, 或 dict of str, 默认为 None
用于追加DataFrame列名称的字符串。
prefix_sep:str, 默认为 ‘_’
如果附加前缀,则使用分隔符/分隔符。或者像这样传递列表或字典prefix。
dummy_na:bool, 默认为 False
如果忽略False NaN。
columns:list-like, 默认为 None
要编码的DataFrame中的列名。如果columns为None,则所有具有的列object或者categorydtype将被转换。
sparse:bool, 默认为 False
dummy-encoded列是否应由a支持SparseArray(True)或常规NumPy数组(False)。
drop_first:bool, 默认为 False
是否通过删除第一个级别从k个分类级别中获取k-1个虚拟对象。
用法举例:
data = pd.DataFrame({"学号":[1,2,3,4],
"录取":["清华","北大","清华","蓝翔"],
"学历":["本科","本科","本科","专科"]})
pd.get_dummies(data)
然后我们在data中将part
和salary
进行处理
我们先加入这些行pd.get_dummies(data.part)
和pd.get_dummies(data.salary)
data = data.join(pd.get_dummies(data.part)).join(pd.get_dummies(data.salary))
data.head()
然后我们将原数据中的part
和salary
删掉
data.drop(columns=['part', 'salary'], inplace=True)
X,Y数据划分
我们需要预测的是这个left这一行,看是否会离职,0是留下,1是离职
先打印一下这一行的具体信息
data.left.value_counts()
0 11428
1 3571
Name: left, dtype: int64
Y
注意如果是一行的话一定要.reshape(-1,1),然后再变成tensor类型
Y_data = data.left.values.reshape(-1, 1)
Y = torch.from_numpy(Y_data).type(torch.FloatTensor)
Y.shape
# torch.Size([14999, 1])
X
这个X就是将原数据中去除掉left这一列的数据,我们可以使用.drop(),或者下面这个方法。
[c for c in data.columns if c != 'left']
X_data = data[[c for c in data.columns if c != 'left']].values
X = torch.from_numpy(X_data).type(torch.FloatTensor)
X.shape
# torch.Size([14999, 20])
创建模型
class Logistic(nn.Module):
def __init__(self):
super().__init__()
self.lin_1 = nn.Linear(20, 64)
self.lin_2 = nn.Linear(64, 64)
self.lin_3 = nn.Linear(64, 1)
self.activate = nn.ReLU()
self.sigmoid = nn.Sigmoid()
def forward(self, input):
x = self.lin_1(input)
x = self.activate(x)
x = self.lin_2(x)
x = self.activate(x)
x = self.lin_3(x)
x = self.sigmoid(x)
return x
或者我们改进一下,写的更简单一点
import torch.nn.functional as F
class Model(nn.Module):
def __init__(self):
super().__init__()
self.liner_1 = nn.Linear(20, 64)
self.liner_2 = nn.Linear(64, 64)
self.liner_3 = nn.Linear(64, 1)
def forward(self, input):
x = F.relu(self.liner_1(input))
x = F.relu(self.liner_2(x))
x = torch.sigmoid(self.liner_3(x))
return x
model=Model()
model
输出
Model(
(liner_1): Linear(in_features=20, out_features=64, bias=True)
(liner_2): Linear(in_features=64, out_features=64, bias=True)
(liner_3): Linear(in_features=64, out_features=1, bias=True)
)
优化器
lr = 0.0001
def get_model():
model = Model()
opt=torch.optim.Adam(model.parameters(), lr=lr)#这里因为我们继承自nn.Module模组,所以可以导入model.parameters()
return model, opt
这里主要优化的是model中的parameters()
参数,之所以可以调用model.parameters()
是因为这个model继承自nn.model
损失函数
loss_fn = nn.BCELoss()
模型和优化器
model, optim = get_model()
划分barch
len(data)
# 14999
batch = 64
no_of_batches = len(data)//batch # 定义全部的批次
epochs = 100
训练
注意这里的with torch.no_grad():
这里我们做预测所以,不需要进行梯度更新,然后就用这个
for epoch in range(epochs):
for i in range(no_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()
with torch.no_grad():
print('epoch:', epoch, 'loss:', loss_fn(model(X), Y).data.item())
Dataset和DataLoader
使用Dataset进行重构数据集
PyTorch有一个抽象的Dataset类。Dataset可以是任何具有__len__函数和__getitem__作为对其进行索引的方法的函数。 本教程将通过示例将自定义HRDataset类创建为的Dataset的子类。
PyTorch的TensorDataset 是一个包装张量的Dataset。通过定义索引的长度和方式,这也为我们提供了沿张量的第一维进行迭代,索引和切片的方法。这将使我们在训练的同一行中更容易访问自变量和因变量。
from torch.utils.data import TensorDataset
HRdataset = TensorDataset(X, Y)
HRdataset
对于这种dataset:
我们可以用img0,target0=dataset[0];img1,target1=dataset[1]
然后我们看看怎么访问它:
HRdataset[1]
(tensor([ 0.8000, 0.8600, 5.0000, 262.0000, 6.0000, 0.0000, 0.0000,
0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000, 0.0000,
1.0000, 0.0000, 0.0000, 0.0000, 0.0000, 1.0000]),
tensor([1.]))
这个就是第一组数据
len(HRdataset)
# 14999
然后有14999组,这是由对于每一个batches访问的时候就是HRdataset[i * batch: i * batch + batch]
。
model, optim = get_model()
for epoch in range(epochs):
for i in range(no_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()
with torch.no_grad():
print('epoch:', epoch, 'loss:', loss_fn(model(X), Y).data.item())
使用DataLoader类进行重构
Pytorch DataLoader负责管理批次。
DataLoader从Dataset创建。
DataLoader使遍历批次变得更容易。DataLoader会自动为我们提供每个小批量。
无需使用 HRdataset[i * batch: i * batch + batch]
对于这个
DataLoader(dataset, batch_size=1, shuffle=False)
必备参数:
dataset:要加载的数据集
常用参数:
batch_size:必须是整数,每个batch里有多少个samples
shuffle = True就会在每个epoch中打乱数据 ,False则不打乱
from torch.utils.data import DataLoader
HR_ds = TensorDataset(X, Y)
HR_dl = DataLoader(HR_ds, batch_size=batch,shuffle=True)
现在,我们的循环更加简洁了,因为(xb,yb)是从数据加载器自动加载的:
for x,y in HR_dl:
pred = model(x)
for epoch in range(epochs):
for x, y in HR_dl:
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())
添加验证
了解过拟合和欠拟合
过拟合:对于训练数据过度拟合,对于未来数据预测很差
欠拟合:对于训练数据拟合不够,对于未来数据预测很差
前面我们只是试图建立一个合理的训练循环以用于我们的训练数据。实际上,您始终还应该具有一个验证集,以识别您是否过度拟合。
训练数据的乱序(shuffle)对于防止批次与过度拟合之间的相关性很重要。另一方面,无论我们是否乱序验证集,验证损失都是相同的。由于shufle需要额外的开销,因此shuffle验证数据没有任何意义。
我们将为验证集使用批大小,该批大小是训练集的两倍。这是因为验证集不需要反向传播,因此占用的内存更少(不需要存储梯度)。我们利用这一优势来使用更大的批量,并更快地计算损失。
train_x, test_x, train_y, test_y = train_test_split(X_data, Y_data, test_size, random_state, shuffle)
train_x:划分的训练集数据
test_x:划分的测试集数据
train_y:划分的训练集标签
test_y:划分的测试集标签
X_data:还未划分的数据集
Y_data:还未划分的标签
test_size:分割比例,默认为0.25,即测试集占完整数据集的比例
random_state:随机数种子,应用于分割前对数据的洗牌。可以是int,RandomState实例或None,默认值=None。设成定值意味着,对于同一个数据集,只有第一次运行是随机的,随后多次分割只要rondom_state相同,则划分结果也相同。
shuffle:是否在分割前对完整数据进行洗牌(打乱),默认为True,打乱
我们划分测试集和验证集
from sklearn.model_selection import train_test_split
train_x, test_x, train_y, test_y = train_test_split(X_data, Y_data)
train_x.shape, train_y.shape, test_x.shape, test_y.shape
# ((11249, 20), (11249, 1), (3750, 20), (3750, 1))
train_ds = TensorDataset(train_x, train_y)
train_dl = DataLoader(train_ds, batch_size=batch, shuffle=True)
test_ds = TensorDataset(test_x, test_y)
test_dl = DataLoader(test_ds, batch_size=batch)
定义计算正确率函数
def accuracy(y_pred, y_true):
y_pred = (y_pred>0.5).type(torch.int32)
acc=(y_pred == y_true).float().mean()
return acc
model, optim = get_model()
for epoch in range(epochs):
for x, y in train_dl:
y_pred = model(x)
loss = loss_fn(y_pred, y)
optim.zero_grad()
loss.backward()
optim.step()
with torch.no_grad():
epoch_accuracy=accuracy(model(train_x),train_y)
epoch_loss=loss_fn(model(train_x),train_y).data
epoch_test_accuracy=accuracy(model(test_x),test_y)
epoch_test_loss=loss_fn(model(test_x),test_y).data
print('epoch:', epoch,
'loss:', epoch_loss.item(),
'accuracy',epoch_accuracy.item(),
'test_loss:', epoch_test_loss.item(),
'test_accuracy',epoch_test_accuracy.item()
)