当前位置:   article > 正文

【深度学习】手把手教你开发自己的深度学习模板从二分类到多分类,从0到1_深度学习多输入二分类模型

深度学习多输入二分类模型


前言

入坑2年后,重新梳理之前的知识,发现其实需要一个自己的深度学习的模板pipeline。他需要:

  1. 数据集切分
  2. dataset的功能
  3. dataloader的功能
  4. train的过程中print 每个epoch的训练集 测试集的准确率,loss
    在这个过程中,我会从自我实现的角度一步步进化,已经说明为什么需要这样做。
    用一个多层感知机为因子搭建一个pipeline

读取数据和处理异常数据用pandas
训练用torch 的tensor是一个好习惯。
计算用numpy

1数据相关

1.1 数据初探

根据15个特征预测员工是否会离职的问题, 很明显是个分类问题,输出是否会离职的概率做分类。


data = pd.read_csv("dataset/HR.csv")
data.head()
  • 1
  • 2
  • 3

在这里插入图片描述
在这里插入图片描述
看一下都有哪些职位:

data.salary.unique()
array(['sales', 'accounting', 'hr', 'technical', 'support', 'management',
       'IT', 'product_mng', 'marketing', 'RandD'], dtype=object)
  • 1
  • 2
  • 3

工资等级:

data.salary.unique()
array(['low', 'medium', 'high'], dtype=object)
  • 1
  • 2

pandas 的group by 功能用一下:按工资和部门分组查询。

data.groupby(["salary","part"]).size()
  • 1

在这里插入图片描述

1.2.数据处理

需要把工资的等级:high low ,…
部门分类:销售 技术 财务 …
转成onehot编码

pd.get_dummies(data.salary)
data = data.join(pd.get_dummies(data.salary))
del data["salary"]
data = data.join(pd.get_dummies(data.part))
del data["part"]
  • 1
  • 2
  • 3
  • 4
  • 5
data.left.value_counts()
  • 1

在这里插入图片描述
问题1:所以为啥需要做One-hot编码?
对于属性是不具备序列性、不能比较大小的属性,通常我们不能用简单的数值来粗暴替换。因为属性的数值大小会影响到权重矩阵的计算,不存在大小关系的属性,其权重也不应该发生相应的变化,那么我们就需要用到One-hot编码(也有人称独热编码)这种特殊的编码方式了。
来看一个简单的例子:假设我们有一个特征是自有房和无自有房,样本情况如下:

 data = [['自有房',40,50000],
        ['无自有房',22,13000],
        ['自有房',30,30000]]
  • 1
  • 2
  • 3

编码后的样本矩阵变为:

 data = [[1,0,40,50000],
        [0,1,22,13000],
        [1,0,30,30000]]
  • 1
  • 2
  • 3

问题2:One-hot编码适用算法,(但是我们这个算法就是逻辑回归在使用的,这块存疑吧)
有大神说,现在的经验,one-hot用在GBDT、XGBoost这些模型里面都挺好的,但是用在逻辑回归里不行。因为逻辑回归要求变量间相互独立,如果你只有一个属性需要做one-hot编码还好,如果你有多个属性需要做one-ont编码,那么当某个样本的多个one-hot属性同时为1时,这两个属性就完全相关了,必然会导致singular error,也就是非奇异矩阵不能求解唯一解,得不出唯一的模型,但是你又不可能把同一个属性的某一个one-hot延伸变量删除。

如果在逻辑回归中入模标称属性,可以直接替换成数值,然后做woe变换,用每个类别的woe值来代替原来的数值,这样既能够避免生成相关性强的变量,又能避开类别间大小无法比较的问题。

1.3 数据变形

构建X_data 和 Y_data
转成torch.tensor 并同意数据到torch.float32
在这里插入图片描述

2 定义网络,优化函数

因为是二分类问题,所以最终需要将线性计算结果,拟合到0,1之间,用sigmoid函数。
因为20个特征,所以选择20,输出的是0,1之间的概率,就是1个特征
二元交叉熵,二分类 当然用二元交叉熵
loss_fn = nn.BCELoss()

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)
        self.sigmoid = nn.Sigmoid()
    
    def forward(self, input):
        x = self.liner_1(input)
        x = F.relu(x)
        x = self.liner_2(x)
        x = F.relu(x)
        x = self.liner_3(x)
        x = self.sigmoid(x)
        return x

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17

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

3. 训练

3.1 可用版本

这里有个关键问题,with no grad 加在哪来的问题,
我们是为了看每一批次后的训练状态,它的梯度是不需要积累的,所以用 with 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)
        # 将model.parameters()
        optim.zero_grad()
        loss.backward()
        optim.step()
    with torch.no_grad():
        print("epoch:",epoch,"loss:",loss_fn(model(X),Y).data.item())

  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15

在这里插入图片描述
优点:简单可用
缺点:

  1. 切片送入模型的过程是手动完成
  2. 没有构建训练集和测试集,
  3. 只能按epoch 看到 整体的loss 而无法看到test accuracy 和loss,也就是说无法验证数据的欠拟合和过拟合

3.2. 优化

基于以上两个缺点,我们会迭代修改它。

3.2.1 使用dataset类进行重构

X ndarray 类型的所有 特征值集合
Y ndarray类型的所有labels 集合

from torch.utils.data import TensorDataset
HRdataset = TensorDataset(X, Y)
HRdataset
  • 1
  • 2
  • 3

可以看到 HRdataset 返回两个数据,x是特征,y是对应的labels
在这里插入图片描述
这样以来,我们就优化了,它的切片。

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())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10

3.2.2 使用dataloader类进一步优化

3.2.1 简化了切片的处理过程,对批次数据的获取产生了优化。但是有一个问题,我们无法shuffle 送入模型的顺序,这样可以避免它基于顺序的过拟合。
这个时候需要一个shuffle函数,把batch_size 当入参传进来了。

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:
        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())
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

3.2.3 使用sklearn进一步优化

test数据集是不需要shuffle的,想一想为什么?

train_x, test_x, train_y, test_y = train_test_split(X_data, Y_data)
train_x = torch.from_numpy(train_x).type(torch.float32)
train_y = torch.from_numpy(train_y).type(torch.float32)
test_x = torch.from_numpy(test_x).type(torch.float32)
test_y = torch.from_numpy(test_y).type(torch.float32)
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)
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9

计算正确率的代码

y_pred = (y_pred > 0.5).type(torch.int32)
(y_pred == labels).float().mean()    #  [True, Fasle, True]
  • 1
  • 2

封装成函数

def accuracy(y_pred, y_true):
    y_pred = (y_pred > 0.5).type(torch.int32)
    acc = (y_pred == y_true).float().mean()
    return acc
  • 1
  • 2
  • 3
  • 4
model, optim = get_model()
  • 1

最终的训练:

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: ', round(epoch_loss.item(), 3),
                                'accuracy:', round(epoch_accuracy.item(), 3),
                                'test_loss: ', round(epoch_test_loss.item(), 3),
                                'test_accuracy:', round(epoch_test_accuracy.item(), 3)
             )
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18

4.总结

一步步的,把从一个最简化版的深度学习模板打造的有模有样,这个时候,对于有监督的分类算法,不管是二分类还是多分批,只需要该自定义网络那块和损失函数,优化目标函数三个地方,别的地方不用动,是不是很酷啊!
一篇文章不能解决所有问题,后面一篇文章,我会加入tensorboard 看板的内容,并最终给出代码,敬请期待。另外,以后我可以指导学生,有偿,欢迎私信。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/我家自动化/article/detail/79207
推荐阅读
相关标签
  

闽ICP备14008679号