pytorch学习(二)

1.logistic回归实战

logistic回归是一种广义线性回归(generalized linear model),与多重线性回归分析有很多相同之处。它们的模型形式基本上相同,都具有 wx + b,其中w和b是待求参数,其区别在于他们的因变量不同,多重线性回归直接将wx+b作为因变量,即y =wx+b,而logistic回归则通过函数L将wx+b对应一个隐状态pp =L(wx+b),然后根据p 与1-p的大小决定因变量的值如果L是logistic函数,就是logistic回归,如果L是多项式函数就是多项式回归。

说的更通俗一点,就是logistic回归会在线性回归后再加一层logistic函数的调用

logistic回归主要是进行二分类预测,我们在激活函数时候讲到过 Sigmod函数,Sigmod函数是最常见的logistic函数,因为Sigmod函数的输出的是是对于0~1之间的概率值,当概率大于0.5预测为1,小于0.5预测为0。

2.Fine tuning 模型微调

针对于某个任务,自己的训练数据不多,那怎么办? 没关系,我们先找到一个同类的别人训练好的模型,把别人现成的训练好了的模型拿过来,换成自己的数据,调整一下参数,再训练一遍,这就是微调(fine-tune)。 PyTorch里面提供的经典的网络模型都是官方通过Imagenet的数据集与训练好的数据,如果我们的数据训练数据不够,这些数据是可以作为基础模型来使用的。

迁移学习 Transfer Learning

迁移学习初衷是节省人工标注样本的时间,让模型可以通过一个已有的标记数据的领域向未标记数据领域进行迁移从而训练出适用于该领域的模型,直接对目标域从头开始学习成本太高,我们故而转向运用已有的相关知识来辅助尽快地学习新知识

3.pytorch处理结构化数据

对于模型的训练,只能够处理数字类型的数据,所以这里面我们首先要将数据分成三个类别

  • 训练的结果标签:即训练的结果,通过这个结果我们就能够明确的知道我们这次训练的任务是什么,是分类的任务,还是回归的任务。
  • 分类数据:这类的数据是离散的,无法通过直接输入到模型中进行训练,所以我们在预处理的时候需要优先对这部分进行处理,这也是数据预处理的主要工作之一
  • 数值型数据:这类数据是直接可以输入到模型中的,但是这部分数据有可能还是离散的,所以如果需要也可以对其进行处理,并且处理后会对训练的精度有很大的提升,这里暂且不讨论
#训练结果
result_var = 'salary'
#分类型数据
cat_names = ['workclass', 'education', 'marital-status', 'occupation', 'relationship', 'race','sex','native-country']
#数值型数据
cont_names = ['age', 'fnlwgt', 'education-num','capital-gain','capital-loss','hours-per-week']

下一步就是要将分类型数据转成数字型数据,在这部分里面,我们还做了对于缺失数据的填充

for col in df.columns:
    if col in cat_names:
        df[col].fillna('---')
        df[col] = LabelEncoder().fit_transform(df[col].astype(str))
    if col in cont_names:
        df[col]=df[col].fillna(0)

我们首先使用了pandas的fillna函数对分类的数据做了空值的填充,这里面标识成一个与其他现有值不一样的值就可以,这里面我使用的三个中划线 --- 作为标记,然后就是使用了sklearn的LabelEncoder函数进行了数据的处理

然后有对我们的数值型的数据做了0填充的处理,对于数值型数据的填充,也可以使用平均值,或者其他方式填充,这个不是我们的重点,就不详细说明了。

数据处理完成后可以看到,现在所有的数据都是数字类型的了,可以直接输入到模型进行训练了.

#分割下训练数据和标签
Y = df['salary']
Y_label = LabelEncoder()
Y=Y_label.fit_transform(Y)
Y
X=df.drop(columns=result_var)
X

以上,基本的数据预处理已经完成了,上面展示的只是一些必要的处理,如果要提高训练准确率还有很多技巧,这里就不详细说明了

定义数据集

要使用pytorch处理数据,肯定要使用Dataset进行数据集的定义,下面定义一个简单的数据集

class tabularDataset(Dataset):
    def __init__(self, X, Y):
        self.x = X.values
        self.y = Y
        
    def __len__(self):
        return len(self.y)
    
    def __getitem__(self, idx):
        return (self.x[idx], self.y[idx])
train_ds = tabularDataset(X, Y)

可以直接使用索引访问定义好的数据集中的数据

train_ds[0]

定义模型

数据已经准备完毕了,下一步就是要定义我们的模型了,这里使用了3层线性层的简单模型作为处理

class tabularModel(nn.Module):
    def __init__(self):
        super().__init__()
        self.lin1 = nn.Linear(14, 500)
        self.lin2 = nn.Linear(500, 100)
        self.lin3 = nn.Linear(100, 2)
        self.bn_in = nn.BatchNorm1d(14)
        self.bn1 = nn.BatchNorm1d(500)
        self.bn2 = nn.BatchNorm1d(100)
        

    def forward(self,x_in):
        #print(x_in.shape)
        x = self.bn_in(x_in)
        x = F.relu(self.lin1(x))
        x = self.bn1(x)
        #print(x)
        
        
        x = F.relu(self.lin2(x))
        x = self.bn2(x)
        #print(x)
        
        x = self.lin3(x)
        x=torch.sigmoid(x)
        return x

在定义模型的时候看到了我们加入了Batch Normalization来做批量的归一化。

训练

#训练前指定使用的设备
DEVICE=torch.device("cpu")
if torch.cuda.is_available():
        DEVICE=torch.device("cuda")
print(DEVICE)
#损失函数
criterion =nn.CrossEntropyLoss()
#实例化模型
model = tabularModel().to(DEVICE)
print(model)
#测试模型是否没问题
rn=torch.rand(3,14).to(DEVICE)
model(rn)
#学习率
LEARNING_RATE=0.01
#BS
batch_size = 1024
#优化器
optimizer = torch.optim.Adam(model.parameters(), lr=LEARNING_RATE)
#DataLoader加载数据
train_dl = DataLoader(train_ds, batch_size=batch_size,shuffle=True)

以上的基本步骤是每个训练过程都需要的,所以就不多介绍了,下面开始进行模型的训练

%%time
model.train()
#训练10轮
TOTAL_EPOCHS=100
#记录损失函数
losses = [];
for epoch in range(TOTAL_EPOCHS):
    for i, (x, y) in enumerate(train_dl):
        x = x.float().to(DEVICE) #输入必须未float类型
        y = y.long().to(DEVICE) #结果标签必须未long类型
        #清零
        optimizer.zero_grad()
        outputs = model(x)
        #计算损失函数
        loss = criterion(outputs, y)
        loss.backward()
        optimizer.step()
        losses.append(loss.cpu().data.item())
    print ('Epoch : %d/%d,   Loss: %.4f'%(epoch+1, TOTAL_EPOCHS, np.mean(losses)))
model.eval()
correct = 0
total = 0
for i,(x, y) in enumerate(train_dl):
    x = x.float().to(DEVICE)
    y = y.long()
    outputs = model(x).cpu()
    _, predicted = torch.max(outputs.data, 1)
    total += y.size(0)
    correct += (predicted == y).sum()
print('准确率: %.4f %%' % (100 * correct / total))

4.计算机视觉

为了使用pytorch的dataloader进行数据的加载,需要先创建一个自定义的dataset

对于自定义的数据集,只需要实现三个函数:

__init__: 初始化函数主要用于数据的加载,这里直接使用pandas将数据读取为dataframe,然后将其转成numpy数组来进行索引

__len__: 返回数据集的总数,pytorch里面的datalorder需要知道数据集的总数的

__getitem__:会返回单张图片,它包含一个index,返回值为样本及其标签。

创建训练和测试集

train_dataset = FashionMNISTDataset(csv_file=DATA_PATH / "fashion-mnist_train.csv")
test_dataset = FashionMNISTDataset(csv_file=DATA_PATH / "fashion-mnist_test.csv")

在使用Pytorch的DataLoader读取数据之前,需要指定一个batch size 这也是一个超参数,涉及到内存的使用量,如果出现OOM的错误则要减小这个数值,一般这个数值都为2的幂或者2的倍数。

#因为是常量,所以大写,需要说明的是,这些常量建议都使用完整的英文单词,减少歧义
BATCH_SIZE=256 # 这个batch 可以在M250的笔记本显卡中进行训练,不会oom

我们接着使用dataloader模块来使用这些数据

train_loader = torch.utils.data.DataLoader(dataset=train_dataset,
                                           batch_size=BATCH_SIZE,
                                           shuffle=True) # shuffle 标识要打乱顺序
test_loader = torch.utils.data.DataLoader(dataset=test_dataset,
                                           batch_size=BATCH_SIZE,
                                           shuffle=False) # shuffle 标识要打乱顺序,测试集不需要打乱

查看一下数据

a=iter(train_loader)
data=next(a)
img=data[0][0].reshape(28,28)
data[0][0].shape,img.shape
plt.imshow(img,cmap = plt.cm.gray)
plt.show()

创建网络

class CNN(NN.Module):
    def __init__(self):
        super(CNN, self).__init__()
        self.layer1 = NN.Sequential(   
            NN.Conv2d(1, 16, kernel_size=5, padding=2),
            NN.BatchNorm2d(16), 
            NN.ReLU()) #16, 28, 28
        self.pool1=NN.MaxPool2d(2) #16, 14, 14
        self.layer2 = NN.Sequential(
            NN.Conv2d(16, 32, kernel_size=3),
            NN.BatchNorm2d(32),
            NN.ReLU())#32, 12, 12
        self.layer3 = NN.Sequential(
            NN.Conv2d(32, 64, kernel_size=3),
            NN.BatchNorm2d(64),
            NN.ReLU()) #64, 10, 10
        self.pool2=NN.MaxPool2d(2)  #64, 5, 5
        self.fc = NN.Linear(5*5*64, 10)
    def forward(self, x):
        out = self.layer1(x)
        #print(out.shape)
        out=self.pool1(out)
        #print(out.shape)
        out = self.layer2(out)
        #print(out.shape)
        out=self.layer3(out)
        #print(out.shape)
        out=self.pool2(out)
        #print(out.shape)
        out = out.view(out.size(0), -1)
        #print(out.shape)
        out = self.fc(out)
        return out

在函数里使用torch.nn提供的模块来定义各个层,在每个卷积层后使用了批次的归一化和RELU激活并且在每一个操作分组后面进行了pooling的操作(减少信息量,避免过拟合),后我们使用了全连接层来输出10个类别。

cnn = CNN();
#可以通过以下方式验证,没报错说明没问题,
cnn(torch.rand(1,1,28,28))
#打印下网络,做最后的确认
print(cnn)

从定义模型开始就要指定模型计算的位置,CPU还是GPU,所以需要加另外一个参数

DEVICE=torch.device("cpu")
if torch.cuda.is_available():
        DEVICE=torch.device("cuda")
print(DEVICE)
#先把网络放到gpu上
cnn=cnn.to(DEVICE)

损失函数

多分类因为使用Softmax回归将神经网络前向传播得到的结果变成概率分布 所以使用交叉熵损失。 在pytorch中 NN.CrossEntropyLoss 是将 nn.LogSoftmax() 和 nn.NLLLoss()进行了整合,CrossEntropyLoss ,我们也可以分开来写使用两步计算,这里为了方便直接一步到位

#损失函数也需要放到GPU中
criterion = NN.CrossEntropyLoss().to(DEVICE)

优化器

#另外一个超参数,学习率
LEARNING_RATE=0.01
#另外一个超参数,指定训练批次
TOTAL_EPOCHS=50
#优化器不需要放GPU
optimizer = torch.optim.Adam(cnn.parameters(), lr=LEARNING_RATE)

开始训练

%%time
#记录损失函数
losses = [];
for epoch in range(TOTAL_EPOCHS):
    for i, (images, labels) in enumerate(train_loader):
        images = images.float().to(DEVICE)
        labels = labels.to(DEVICE)
        #清零
        optimizer.zero_grad()
        outputs = cnn(images)
        #计算损失函数
        loss = criterion(outputs, labels)
        loss.backward()
        optimizer.step()
        losses.append(loss.cpu().data.item());
        if (i+1) % 100 == 0:
            print ('Epoch : %d/%d, Iter : %d/%d,  Loss: %.4f'%(epoch+1, TOTAL_EPOCHS, i+1, len(train_dataset)//BATCH_SIZE, loss.data.item()))

训练后操作

可视化损失函数

plt.xkcd();
plt.xlabel('Epoch #');
plt.ylabel('Loss');
plt.plot(losses);
plt.show();

保存模型

torch.save(cnn.state_dict(), "fm-cnn3.pth")
# 加载用这个
#cnn.load_state_dict(torch.load("fm-cnn3.pth"))

模型评估

模型评估就是使用测试集对模型进行的评估,应该是添加到训练中进行了,这里为了方便说明直接在训练完成后评估了

cnn.eval()
correct = 0
total = 0
for images, labels in test_loader:
    images = images.float().to(DEVICE)
    outputs = cnn(images).cpu()
    _, predicted = torch.max(outputs.data, 1)
    total += labels.size(0)
    correct += (predicted == labels).sum()
print('准确率: %.4f %%' % (100 * correct / total))

模型评估的步骤如下:

  1. 将网络的模式改为eval。
  2. 将图片输入到网络中得到输出。
  3. 通过取出one-hot输出的最大值来得到输出的 标签。
  4. 统计正确的预测值。

进一步优化

%%time
#修改学习率和批次
cnn.train()
LEARNING_RATE=LEARNING_RATE / 10
TOTAL_EPOCHS=20
optimizer = torch.optim.Adam(cnn.parameters(), lr=0.001)
losses = [];
for epoch in range(TOTAL_EPOCHS):
    for i, (images, labels) in enumerate(train_loader):
        images = images.float().to(DEVICE)
        labels = labels.to(DEVICE)
        #清零
        optimizer.zero_grad()
        outputs = cnn(images)
        #计算损失函数
        #损失函数直接放到CPU中,因为还有其他的计算
        loss = criterion(outputs, labels).cpu()
        loss.backward()
        optimizer.step()
        losses.append(loss.data.item());
        if (i+1) % 100 == 0:
            print ('Epoch : %d/%d, Iter : %d/%d,  Loss: %.4f'%(epoch+1, TOTAL_EPOCHS, i+1, len(train_dataset)//BATCH_SIZE, loss.data.item()))

再次进行评估

损失小了,但是准确率没有提高,这就说明已经接近模型的瓶颈了,如果再要进行优化,就需要修改模型了。另外还有一个判断模型是否到瓶颈的标准,就是看损失函数,最后一次的训练的损失函数明显的没有下降的趋势,只是在震荡,这说明已经没有什么优化的空间了。

通过简单的操作,我们也能够看到Adam优化器的暴力性,我们只要简单的修改学习率就能够达到优化的效果,Adam优化器的使用一般情况下是首先使用0.1进行预热,然后再用0.01进行大批次的训练,最后使用0.001这个学习率进行收尾,再小的学习率一般情况就不需要了。

BATCH_SIZE: 批次数量,定义每次训练时多少数据作为一批,这个批次需要在dataloader初始化时进行设置,并且需要这对模型和显存进行配置,如果出现OOM有线减小,一般设为2的倍数

DEVICE:进行计算的设备,主要是CPU还是GPU

LEARNING_RATE:学习率,反向传播时使用

TOTAL_EPOCHS:训练的批次,一般情况下会根据损失和准确率等阈值

其实优化器和损失函数也算超参数,这里就不说了

  • 3
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值