建立网络全过程

本文介绍了如何使用PyTorch构建自己的`MyDataset`类,对图片进行数据增强如resize、RandomResizedCrop和标准化。着重讲解了如何处理训练集中样本不平衡问题,通过`ImageFolder`与`WeightedRandomSampler`进行权重分配。此外,还详细阐述了网络创建、性能评估指标、模型训练与验证流程。
摘要由CSDN通过智能技术生成

读取数据,构建数据

项目1 创建自己的dataset类

transform 对图片进行处理

碎片

  1. 对图片进行数据增强处理
 self.transform = transforms.Compose(
            [
                transforms.Resize(size = (224,224)),#尺寸规范 #将图片设置为224*224
                transforms.RandomResizedCrop(200), #随机   随机长宽比裁剪
                transforms.ToTensor(),   #转化为tensor #那transform.Normalize()是怎么工作的呢?以上面代码为例,ToTensor()能够把灰度范围从0-255变换到0-1之间,
                transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),#而后面的transform.Normalize()则把0-1变换到(-1,1).具体地说,对每个通道而言,Normalize执行以下操作:image=(image-mean)/std  其中mean和std分别通过(0.5,0.5,0.5)和(0.5,0.5,0.5)进行指定。原来的0-1最小值0则变成(0-0.5)/0.5=-1,而最大值1则变成(1-0.5)/0.5=1.
            ])

对参数进行解释

函数解释
Resize改变大小
RandomResizedCrop随便改变比例,并随机选取中心,设定大小为200v200
ToTensor图像转tensor
Normalize对图像标准化
修改__getitem__
  1. 将测试集(不带标签的数据图像)label设定为文件名(名字必须是数字)

这里对__getitem__进行重写

  def __getitem__(self, idx: int):
        # img to tensor and label to tensor
        print(idx)
        img_path = self.path_list[idx]#  __getitem__给定  self和一个key
        print(img_path)
        if self.train_flag is True:
            if img_path.split('.')[0] == 'dog' : 
                label = 1
            else:
                label = 0
        else:
            label = int(img_path.split('.')[0]) # split 的是str类型要转换为int
        label = torch.as_tensor(label, dtype=torch.int64) # 必须使用long 类型数据,否则后面训练会报错 expect long
        img_path = os.path.join(self.data_path, img_path)
        img = Image.open(img_path).convert('RGB')
        img = self.transform(img)
        return img, label

总览

class MyDataset(Dataset):#创建自己的dataset类  ,继承dataset  
    def __init__(self, data_path:str, train=True, transform=None):#init初始化方案  指定path的类型是str  train默认是True  transform 默认是无
        self.data_path = data_path  #设定位置
        self.train_flag = train     #train的参数
        if transform is None:   
            self.transform = transforms.Compose(
            [
                transforms.Resize(size = (224,224)),#尺寸规范 #将图片设置为224*224
                transforms.RandomResizedCrop(200), #随机   随机长宽比裁剪
                transforms.ToTensor(),   #转化为tensor #那transform.Normalize()是怎么工作的呢?以上面代码为例,ToTensor()能够把灰度范围从0-255变换到0-1之间,
                transforms.Normalize((0.5, 0.5, 0.5), (0.5, 0.5, 0.5)),#而后面的transform.Normalize()则把0-1变换到(-1,1).具体地说,对每个通道而言,Normalize执行以下操作:image=(image-mean)/std  其中mean和std分别通过(0.5,0.5,0.5)和(0.5,0.5,0.5)进行指定。原来的0-1最小值0则变成(0-0.5)/0.5=-1,而最大值1则变成(1-0.5)/0.5=1.
            ])
        else:
            self.transform = transform
        self.path_list = os.listdir(data_path)#给出一个文件夹所有的文件
    def __getitem__(self, idx: int):
        # img to tensor and label to tensor
        print(idx)
        img_path = self.path_list[idx]#  __getitem__给定  self和一个key
        print(img_path)
        if self.train_flag is True:
            if img_path.split('.')[0] == 'dog' : 
                label = 1
            else:
                label = 0
        else:
            label = int(img_path.split('.')[0]) # split 的是str类型要转换为int
        label = torch.as_tensor(label, dtype=torch.int64) # 必须使用long 类型数据,否则后面训练会报错 expect long
        img_path = os.path.join(self.data_path, img_path)
        img = Image.open(img_path).convert('RGB')
        img = self.transform(img)
        return img, label
    def __len__(self) -> int:  #没有这个指定,就没有len(class)  也就是 没有len(MyDataset)
        return len(self.path_list)

把测试集与训练集的文件路径输入

train_path =r'E:\machine_learning\mcm\new_net\Math-Modeling-2021-master\data\pic\big_train'
test_path = r'E:\machine_learning\mcm\new_net\Math-Modeling-2021-master\data\pic\test_N'
train_ds =MyDataset(train_path)
test_ds = MyDataset(test_path,train=False)
将数据集输入DataLoader
train_loader = torch.utils.data.DataLoader(train_ds, batch_size=32,
                                            shuffle=True, pin_memory=True, num_workers=0)
test_loader = torch.utils.data.DataLoader(test_ds, batch_size=32,
                                            shuffle=True, pin_memory=True, num_workers=0)
#pin_memory If True, the data loader will copy tensors into CUDA pinned memory before returning them. 也就是一个数据拷贝的问题
## numworkers设置不为0 会报错 Broken pipe Error 网上说是win10上的pytorch bug

pin_memory 拷贝数据

项目2 使用自带的

ImageFolder

如果使用torch自带的数据集整理功能,那就用ImageFolder

transform = transforms.Compose([transforms.RandomResizedCrop(200),
                                transforms.RandomHorizontalFlip(),
                                transforms.ToTensor(),
                                transforms.Normalize(mean=(.5, .5, .5),
                                                     std=(.5, .5, .5))])

trainData = dsets.ImageFolder(train_path, transform=transform)
testData = dsets.ImageFolder(test_path, transform=transform)
防止样本不均衡,使用sample进行权重分配

使用ImageFoldertargets获取一部分数据对应的标签:
class_sample_count表示每种种类的个数
samples_weight表示样本的权重

target = np.array(trainData.targets)
class_sample_count = np.array([len(np.where(target == t)[0]) for t in np.unique(target)])
# print(ctest_sample_count)
samples_weight = np.array([(1. / class_sample_count)[t] for t in target])
samples_weight = torch.from_numpy(samples_weight).double()
sampler = WeightedRandomSampler(samples_weight, len(samples_weight))

trainLoader = DataLoader(dataset=trainData, batch_size=BATCH_SIZE, sampler=sampler)
testLoader = DataLoader(dataset=testData, batch_size=BATCH_SIZE, shuffle=False)

创建网络

import torch.nn.functional as F
class MyCNN(nn.Module):
    def __init__(self):
        super(MyCNN,self).__init__()# 这是对继承自父类的属性进行初始化,而且是用父类的初始化方法来初始化继承的属性.也就是说,子类继承了父类的所有属性和方法,
        self.conv1 = nn.Conv2d(3,8,kernel_size=3,stride=1,padding=1) # 按照公式计算后经过卷积层不改变尺寸 #对由多个输入平面组成的输入信号进行二维卷积  3通道 卷积产生8通道 卷积核尺寸为3v3矩阵1步长padding 填充
        self.pool = nn.MaxPool2d(2,2) # 2*2的池化 池化后size 减半
        self.conv2 = nn.Conv2d(8,16,kernel_size=3,stride=1,padding=1)
        self.fc1 = nn.Linear(16*56*56,256)#两个池化,所以是224/2/2=56
        self.fc2 = nn.Linear(256,64)
        self.fc3 = nn.Linear(64,2)
#         self.dp = nn.Dropout(p=0.5)
    def forward(self,x):
#         print("input:", x)
        x = self.pool(F.relu(self.conv1(x)))
#         print("first conv:", x)
        x = self.pool(F.relu(self.conv2(x)))
#         print("second conv:", x)
             
        x = x.view(-1, 16 * 56* 56)#将数据平整为一维的 
        x = F.relu(self.fc1(x))
        x = F.relu(self.fc2(x))  
        x = self.fc3(x)  
#         x = F.log_softmax(x,dim=1) NLLLoss()才需要,交叉熵不需要
        return x

性能评估


criterion = nn.CrossEntropyLoss()
# criterion = nn.BCELoss()  #二分类交叉熵损失函数
# criterion = nn.BCEWithLogitsLoss() #二分类交叉熵损失函数 带log loss
# criterion = nn.MSELoss()

optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
#也可以选择Adam优化方法
# optimizer = torch.optim.Adam(net.parameters(),lr=1e-2)   
计算函数
class AvgrageMeter(object):

    def __init__(self):
        self.reset()

    def reset(self):
        self.avg = 0
        self.sum = 0
        self.cnt = 0

    def update(self, val, n=1):
        self.sum += val * n
        self.cnt += n
        self.avg = self.sum / self.cnt
准确率函数

topk=(1,)这个参数设置,是在输入的时候还会收入一个数,然后在maxk=max(topk)中会比对大小,选最大的假设咱做top2准确率,那就输入个2

output.topk(maxk,1,True,True) 输入参数对应output.topk(k=maxk,dim=1,largest=True,sorted=True)
output.topk输出是值value和索引index
value, index= output.topk()
pred.eq()比对label通过索引pred,对两个张量Tensor进行逐元素的比较,若相同位置的两个元素相同,则返回True;若不同,返回False

## topk的准确率计算
def accuracy(output, label, topk=(1,)):#topk还可以再输入一个数
    maxk = max(topk) 
    batch_size = label.size(0)
    
    # 获取前K的索引
    _, pred = output.topk(maxk, 1, True, True) #使用topk来获得前k个的索引
    pred = pred.t() # 进行转置
    # eq按照对应元素进行比较 view(1,-1) 自动转换到行为1,的形状, expand_as(pred) 扩展到pred的shape
    # expand_as 执行按行复制来扩展,要保证列相等
    correct = pred.eq(label.view(1, -1).expand_as(pred)) # 与正确标签序列形成的矩阵相比,生成True/False矩阵
    
#     print(correct)

    rtn = []
    for k in topk:
        correct_k = correct[:k].contiguous().view(-1).float().sum(0) # 前k行的数据 然后平整到1维度,来计算true的总个数
        rtn.append(correct_k.mul_(100.0 / batch_size)) # mul_() ternsor 的乘法  正确的数目/总的数目 乘以100 变成百分比
    return rtn

训练模型

训练函数
def train( epoch, train_loader, device, model, criterion, optimizer,tensorboard_path):
    model = model.to(device)
    for e in range(epoch):
        model.train()
        top1 = AvgrageMeter()
        train_loss = 0.0
        train_loader = tqdm(train_loader)  #转换成tqdm类型 以方便增加日志的输出
        train_loader.set_description('[%s%04d/%04d %s%f]' % ('Epoch:', e + 1, epoch, 'lr:', 0.001))
        for i, data in enumerate(train_loader, 0):  # 0是下标起始位置默认为0
            inputs, labels = data[0].to(device), data[1].to(device)
            # 初始为0,清除上个batch的梯度信息
            optimizer.zero_grad()
            outputs = model(inputs)
            loss = criterion(outputs,labels)
            loss.backward()
            optimizer.step()
            # topk 准确率计算
            prec1, prec2 = accuracy(outputs, labels, topk=(1, 2))
            n = inputs.size(0)
            top1.update(prec1.item(), n)
            train_loss += loss.item()
            postfix = {'train_loss': '%.6f' % (train_loss / (i + 1)), 'train_acc': '%.6f' % top1.avg}
            train_loader.set_postfix(log=postfix)

            # ternsorboard 曲线绘制
            writer = SummaryWriter(tensorboard_path)
            writer.add_scalar('Train/Loss', loss.item(), epoch)
            writer.add_scalar('Train/Accuracy', top1.avg, epoch)
            writer.flush()

    print('Finished Training')
验证函数
def validate( epoch, validate_loader, device, model, criterion):
    val_acc = 0.0
    model = model.to(device)
    for e in range(epoch):
        model.eval()
        with torch.no_grad():  # 进行评测的时候网络不更新梯度
            val_top1 = AvgrageMeter()
            validate_loader = tqdm(validate_loader)
            validate_loss = 0.0
            for i, data in enumerate(validate_loader, 0):  # 0是下标起始位置默认为0
                inputs, labels = data[0].to(device), data[1].to(device)
                #         inputs,labels = data[0],data[1]
                outputs = model(inputs)
                loss = criterion(outputs, labels)

                prec1, prec2 = accuracy(outputs, labels, topk=(1, 2))
                n = inputs.size(0)
                val_top1.update(prec1.item(), n)
                validate_loss += loss.item()
                postfix = {'validate_loss': '%.6f' % (validate_loss / (i + 1)), 'validate_acc': '%.6f' % val_top1.avg}
                validate_loader.set_postfix(log=postfix)
                writer = SummaryWriter(tensorboard_tpath)
                writer.add_scalar('Validate/Loss', loss.item(), epoch)
                writer.add_scalar('Validate/Accuracy', val_top1.avg, epoch)
                writer.flush()
            val_acc = val_top1.avg
    return val_acc
保存
def submission(csv_path,test_loader, device, model):
    result_list = []
    model = model.to(device)
    test_loader = tqdm(test_loader)
    with torch.no_grad():  # 进行评测的时候网络不更新梯度
        for i, data in enumerate(test_loader, 0):
            images, labels = data[0].to(device), data[1].to(device)
#             print(images)
            
#             print(labels)
            outputs = model(images)
            softmax_func = nn.Softmax(dim=1)  # dim=1表示行的和为1
         
            soft_output = softmax_func(outputs)

            predicted = soft_output[:, 1]

            for i in range(len(predicted)):
                result_list.append({
                    "id": labels[i].item(),
                    "label": predicted[i].item()
                })
            
    # 从list转成 dataframe 然后保存为csv文件
    columns = result_list[0].keys()
    result_dict = {col: [anno[col] for anno in result_list] for col in columns}
    result_df = pd.DataFrame(result_dict)
    result_df = result_df.sort_values("id")
    result_df.to_csv(csv_path, index=None)
# net =  MyCNN()
net =resnet18
device = torch.device("cuda:0" if torch.cuda.is_available() else "cpu")
criterion = nn.CrossEntropyLoss()
# criterion = nn.BCELoss()  #二分类交叉熵损失函数
# criterion = nn.BCEWithLogitsLoss() #二分类交叉熵损失函数 带log loss
# criterion = nn.MSELoss()

optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
#也可以选择Adam优化方法
# optimizer = torch.optim.Adam(net.parameters(),lr=1e-2)   

# train( 1, train_loader, device,net, criterion, optimizer,tensorboard_path) # 完整的训练数据集
train( 30, new_train_loader, device,net, criterion, optimizer,tensorboard_path) # 划分80%后的训练数据集
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值