《PyTorch深度学习实践》 第8讲

# 直接复制到编译器里面看就行
# 视频来源(b站刘二大人):08.加载数据集_哔哩哔哩_bilibili

import torch
import numpy as np
import matplotlib.pyplot as plt
import time
"""
Dataset是一个抽象类,无法实例化对象,只能被其他的子类继承
我们自己要定义的话得先继承Dataset类,然后自己去定义实例化对象
Dataset是为了拿出数据的索引以及数据的长度(数据样本的个数)
"""
from torch.utils.data import Dataset
"""
DataLoader是我们自己用来加载数据的类,比如做shuffle(随机化数据),做batch-size
DataLoader是为了拿出mini-batch,来在训练的时候快速地去用
"""
from torch.utils.data import DataLoader


# prepare dataset

"""
DiabetesDataset是我们自己定义的类,继承自Dataset,继承了一些父类的基本功能
然后我们需要在里面实现几个自己的方法
"""
class DiabetesDataset(Dataset):
    """
    构造方法:
    1.加载入所有数据,这个前提就是数据集较小,比如下面的糖尿病数据集就是一个关系表,占用内存并不大
    2.加载一批数据,例如图像数据,图像数据一般都比较大,几十个g,在init里面全部加载进去不现实
    """
    # 实例化对象时传入dataset的文件具体地址,如果在当前文件夹下面,就直接写文件名称即可
    def __init__(self, filepath):
        # np.loadtxt():用于从文本加载数据
        xy = np.loadtxt(filepath, # 数据集文件路径
                        delimiter=',',  # 以','作为分隔符隔开数据
                        dtype=np.float32) # 以32位浮点数读取

        # self.len是数据的行数,为了之后魔术方法__len__使用,可以通过len(实例化类名称)返回数据的条数
        self.len = xy.shape[0]  # shape(多少行,多少列)
        """
        torch.from_numpy方法就是把numpy类型的转换为tensor类型,并且操作的是同一块内存
        """
        # x_data是除标签以外的所有数据,标签是最后一列
        self.x_data = torch.from_numpy(xy[:, :-1])
        # y_data就是最后一列标签
        self.y_data = torch.from_numpy(xy[:, [-1]])

    """
    __getitem__这个方法是在实例化对象之后,这个对象支持下标操作,通过一个索引,把里面的数据拿出来
    """
    def __getitem__(self, index):
        """
        :param index: 实例化对象后面所跟的索引
        :return:返回的是两个内容,第一个内容是x_data的一行数据,第二个内容是这一行数据对应的标签,返回的是一个元组
        """
        return self.x_data[index], self.y_data[index]

    """
    下面的这个魔法方法的作用:将来实例化对象之后,可以通过len(实例化的对象名称)返回我所需要返回的数字
                        这里返回的是加载数据集的行数,即数据集里面的数据条数
    """
    def __len__(self):
        return self.len

"""
下面这个是dataset最重要的两个部分:
1.可以通过 dataset[索引] 拿出数据集相应位置的数据
2.可以通过 len(实例化对象) 拿出数据集的数据个数
"""


# 首先实例化对象,对象名就叫dataset,将diabetes.csv这个文件里面的所有数据进行传入处理,转换为tensor类型
dataset = DiabetesDataset('diabetes.csv')
# 实例化DataLoader对象,对象名称叫做train_loader,batch_size训练的一批数据的大小,shuffle就是是否打乱数据,这后面用的是小批量梯度下降算法
# 这个train_loader实例化对象就是处理完的dataset,DataLoader字面意思,数据加载器,也就是加载处理完的数据集(分批次,打乱之类的处理)
# 只要下面的dataset支持索引和输出长度,那么DataLoader就支持数据的小批量的生成,之后通过for循环把数据拿出来训练
train_loader = DataLoader(dataset=dataset, # 传入数据集
                          batch_size=32, #
                          shuffle=True, # 打乱数据
                          num_workers=4)  # num_workers 多线程,提高读取的效率


# design model using class

class Model(torch.nn.Module):
    def __init__(self):
        super(Model, self).__init__()
        self.linear1 = torch.nn.Linear(8, 128)
        self.linear2 = torch.nn.Linear(128, 64)
        self.linear3 = torch.nn.Linear(64, 1)
        self.activate = torch.nn.ReLU() # 激活函数
        self.sigmoid = torch.nn.Sigmoid()

    def forward(self, x):
        x = self.activate(self.linear1(x)) # 先是线性层,然后对线性层进行激活函数非线性变换
        x = self.activate(self.linear2(x))
        x = self.sigmoid(self.linear3(x))
        return x

# 实例化一个Model类,名称为model
model = Model()

# construct loss and optimizer
"""
BCELoss:二分类交叉熵损失
具体见:https://www.jianshu.com/p/63e255e3232f
"""
criterion = torch.nn.BCELoss(reduction='mean')
"""
下面就是梯度下降算法优化器,第一个参数为参数组,lr为初始学习率0.01
详情见:https://blog.csdn.net/qq_41917697/article/details/119059824
或者文件夹里面的pdf文档
"""
optimizer = torch.optim.SGD(model.parameters(), lr=0.01)

# 创建两个list用来存储epoch和最后一个loss
epoch_num = []
loss_num = []

# 写一个求列表平均值的函数
def mean_num_list(list):
    sum = 0
    for num in list:
        sum += num
    return sum * 0.1 / len(list)

# training cycle forward, backward, update
if __name__ == '__main__':
    """
    如果只用一个样本来随机梯度下降,可以得到一个比较好的随机性,用来克服鞍点的问题,但是这样的话优化的时间会很长,因为每一次只用一个样本
    而全部batch的好处就是可以最大化向量计算优势,来提升它的计算速度,但是在计算性能上会出现问题,遇到鞍点之类的无法克服
    所以在深度学习里面会用到mini_batch的方法,来均衡训练性能和训练时间上的需求
    1.epoch:One forward pass(前向传播) and one backward pass(反向传播) of all the training examples(所有的训练样本).
    "所有样本都进行了训练就叫epoch"
    2.Batch-Size:The number of training examples in one forward backward pass.
    "每次训练的时候所用的样本数量,一次前馈,一次反馈,一次更新所用的样本数量"
    3.Iterations:Number of passes, each pass using [batch size] number of examples.
    "每一次迭代都用一个mini-batch,Iterations等于 样本数量 / batch-size"
    """
    # 采用mini-batch的形式的话,循环得写成嵌套循环,外层是epoch的个数,即总共遍历了几次样本
    # 内层是对mini-batch进行的训练,假设有1000个样本,batch-size=200,那么就是循环5次完成一个epoch
    start_time = time.time()
    for epoch in range(15):
    # 数据集里面的所有数据跑完1轮就叫一个epoch,这里的意思是需要跑100轮
        epoch_num.append(epoch)

        temp_list = []


        # train_loader使用了多线程,在windows下使用会报错,所以把它包在if __name__ == '__main__'里面,这样就不会报错了
        for i, data in enumerate(train_loader, 0):  # train_loader 是先shuffle后mini_batch
        # enumerate转换为枚举类型,后面那个数字是从索引0开始,可以获得索引和索引所对应的数据
        # i为索引,data为所对应的数据
            # 1.prepare data
            # data包含了每一个mini-batch的数据特征向量(inputs)以及对应的标签(labels)
            # batch_size是多少,包含的特征向量个数以及数据标签个数就是几
            inputs, labels = data # DataLoader加载完的数据在这里拿出来之后都是tensor类型的


            # 2.forward
            y_pred = model(inputs)
            # 损失函数
            loss = criterion(y_pred, labels)
            print(epoch, i, loss.item())
            temp_list.append(loss.item())
            if i == 23:
                loss_num.append(mean_num_list(temp_list))


            # 3.backward
            optimizer.zero_grad() # 清空过往的梯度,如果不清空,那么梯度会累加起来
            loss.backward()       # 计算当前的梯度

            # 4.update更新参数
            optimizer.step() # 根据梯度来更新参数
        print(epoch)
    #print(len(epoch_num),len(loss_num))
    end_time = time.time()
    print(end_time - start_time)
    plt.plot(epoch_num, loss_num)
    plt.ylabel('loss')
    plt.xlabel('epoch')
    plt.show()

你可能感兴趣的:(深度学习,pytorch,python)