1.食物分类问题说明

有11类食物,其中带标签的有280×11张,无标签输出有6786张,验证集30*11张,测试集有3347张。
带标签的数据训练为监督学习,不带标签的数据训练为半监督学习。由上文分类任务的原理可以训练出一个模型,用于食物分类。
半监督学习
当使用有标签和无标签的数据时,我们可以采用半监督学习。原理就是先用有标签的数据训练模型,当模型达到一定准确度时,将无标签数据通过模型,得到预测的标签y,当预测的准确率超过0.99时,将无标签数据打上标签,加入训练集列表中进行训练。当然,也不是每一轮都尝试采用无标签数据集,不然每一轮都要读无标签数据,效率低。每训练五轮处理一次无标签数据,尝试获得可靠的无标签数据集作为训练集。

2.代码说明

引用的函数库

import random
import torch
import torch.nn as nn
import numpy as np
import os

from torch.utils.data import DataLoader , Dataset
from PIL import Image            #读取图片数据
from tqdm import tqdm         #可以看看读到哪里了
from torchvision import transforms   #数据增广

import time
import matplotlib.pyplot as plt

from model_utils.model import initialize_model

seed的作用是将所有随机数都固定,即每次随机到的都是同一个数

def seed_everything(seed):#让所有随机都固定下来
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.deterministic = True
    random.seed(seed)
    np.random.seed(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)
#################################################################
seed_everything(0)
###############################################
HW = 224#图片尺寸改为224*224

对训练集的数据增强
由于机器很笨,一张图片放大缩小后,同样的卷积核卷出来的特征是不同的,机器会不认识,于是在训练的时候就将每一张照片都进行变换,让机器见过所有不同的情况。
transforms提供了许多图片变换的函数,比如裁剪、旋转、缩放等等。

tran_transform = transforms.Compose(
    [
        transforms.ToPILImage(),              #将图像转化为PIL图像
        transforms.RandomResizedCrop(224),    #随机放大裁剪
        transforms.RandomRotation(50),        #旋转50
        transforms.ToTensor()				  #将224,224,3转换为模型可接受的3,224,224
    ]
)

对验证集的数据处理
验证的时候不要改变图像,就是使用原本的图像进行预测

val_transform = transforms.Compose(
    [
        transforms.ToPILImage(),              
        transforms.ToTensor()
    ]
)

数据处理的三个部分

class food_Dataset(Dataset):

init部分
对于不同的模式有不同的数据读取要求

    def __init__(self,path,mode = "train"):#默认为训练模式
        self.mode = mode
        if mode == "semi":#对于半监督模式,仅读取X
            self.X = self.read_file(path)
        else:
            self.X,self.Y = self.read_file(path)#其他模式都需要X和Y
            self.Y = torch.LongTensor(self.Y)#将标签转化为长整型
        if mode == "train":                
            self.transform = tran_transform#调用tran_transform函数处理数据
        else:
            self.transform = val_transform#调用val_transform函数处理数据

读数据部分

    def read_file(self,path):  # 读文件

        if self.mode == "semi":
            file_list = os.listdir(path)  # 读出文件夹下所有文件名字
            xi = np.zeros((len(file_list), HW, HW, 3), dtype=np.uint8)  # 创建一个数组存图片和标签,其中dtype指定数据类型为8位整数类型,方便计算
            for j, img_name in enumerate(file_list):
                img_path = os.path.join(path, img_name)#构建完整的路径
                img = Image.open(img_path)  # 将图片读入进来
                img = img.resize((HW, HW))#调整照片尺寸
                xi[j, ...] = img  # 将照片数据存入数组中...表示后面尺寸不变
            print("读到了%d个训练数据" % len(xi))
            return xi


        else:
            for i in tqdm(range(11)):#遍历11个类别
                file_dir = path + "/%02d" % i#构建完整的路径
                file_list = os.listdir(file_dir)  # 读出文件夹下所有文件名字

                xi = np.zeros((len(file_list), HW, HW, 3), dtype=np.uint8)  # 存图片和标签
                yi = np.zeros(len(file_list), dtype=np.uint8)

                for j, img_name in enumerate(file_list):
                    img_path = os.path.join(file_dir, img_name)
                    img = Image.open(img_path)  # 将图片读入进来
                    img = img.resize((HW, HW))
                    xi[j, ...] = img  # ...表示后面尺寸不变
                    yi[j] = i

                # 将11类图片放在一个大矩阵里
                if i == 0:  # 如果是0类,则让0类作为X,Y,其他类往后接
                    X = xi
                    Y = yi
                else:
                    X = np.concatenate((X, xi), axis=0)  # 应为要竖着接,故axis = 0
                    Y = np.concatenate((Y, yi), axis=0)
            print("读到了%d个训练数据" % len(Y))
            return X, Y

getitim部分

 def __getitem__(self, item):
        if self.mode == "semi":  #半监督模式
            return self.transform(self.X[item]), self.X[item]#仅返回X
        else:
            return self.transform(self.X[item]),self.Y[item]

len部分

    def __len__(self):
        return len(self.X)

半监督数据处理部分

class semiDataset(Dataset):

初始化参数

    def __init__(self,no_label_loder,model,device,thres=0.99):#置信度为0.99
        x,y = self.get_label(no_label_loder,model,device,thres)#获得伪标签
        if x == []:      #没有伪标签
            self.flag = False

        else:#有伪标签,
            self.flag = True
            self.X = np.array(x)             #将X转化位numpy数组      
            self.Y = torch.LongTensor(y)     #Y转化为张量
            self.transform = tran_transform  #数据转换

获得无标签数据作为训练集

  def get_label(self,no_label_loder,model,device,thres):
        model = model.to(device)
        pred_prob = []   #存储预测的最大概率
        labels = []		 #存储预测的标签
        x = []			 #存储输入数据
        y = []			 #存储伪标签
        soft = nn.Softmax()   #将模型的输出转化为概率分布

        with torch.no_grad():
            for bat_x, _ in no_label_loder:#遍历无标签数据
                bat_x = bat_x.to(device)   #放设备上
                pred = model(bat_x)		   #获得预测数据
                pred_soft = soft(pred)	   #转化为概率分布
                pred_max,pred_value = pred_soft.max(1)#返回最后结果中最大值和最大值的下标
                pred_prob.extend(pred_max.cpu().numpy().tolist())#pred_max转化为列表
                labels.extend(pred_value.cpu().numpy().tolist())#将labels转化为列表
        for index,prob in enumerate(pred_prob):#遍历最大的概率
            if  prob > thres:
                x.append(no_label_loder.dataset[index][1])  #  调用到原始的getitem
                y.append(labels[index])

        return x,y

半监督的getinit部分和len部分

    def __getitem__(self, item):
        return self.transform(self.X[item]),self.Y[item]
    def __len__(self):
        return len(self.X)

半监督数据加载器

def get_semi_loader(no_label_loder,model,device,thres):
    semiset = semiDataset(no_label_loder,model,device,thres)
    if semiset.flag == False:#没有置信度超过0.99的数据
        return None
    else:                    #有可置信的数据,返回数据
        semi_loader = DataLoader(semiset,batch_size=16,shuffle=False)
        return semi_loader

各种路径

no_lable_path =r"E:\深度学习相关\第四五节_分类代码 (1)\food_classification\food-11_sample\training\unlabeled\00"#r是去出转义字符
train_path =r"E:\深度学习相关\第四五节_分类代码 (1)\food_classification\food-11_sample\training\labeled"#r是去出转义字符
val_path =r"E:\深度学习相关\第四五节_分类代码 (1)\food_classification\food-11_sample\validation"#r是去出转义字符
#三种模式数据集实例
train_set = food_Dataset(train_path,"train")
val_set = food_Dataset(val_path,"val")
no_lable_set = food_Dataset(no_lable_path,"semi")

train_loader = DataLoader(train_set,batch_size=16, shuffle=True)# 创建训练数据加载器
val_loader = DataLoader(val_set,batch_size=16, shuffle=True)# 创建验证数据加载器
no_lable_loader = DataLoader(no_lable_set,batch_size=16, shuffle=False)## 创建无标签数据加载器,这里一定不要打乱

自己设置的一个模型
将3 * 224 * 224——>512 * 7 * 7再拉直进行分类,其中包含4层卷积。

class myModel(nn.Module):
    def __init__(self,num_class):
        super(myModel, self).__init__()
        #3 * 224 *224   ->   512*512   ->   拉直   ->   全连接
        self.conv1 = nn.Conv2d(3,64,3,1,1)         #64*224*224
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU()
        self.pool1 = nn.MaxPool2d(2)       #64*112*112

        self.layer1 = nn.Sequential(
        nn.Conv2d(64, 128, 3, 1, 1)  ,# 128*112*112
        nn.BatchNorm2d(128),
        nn.ReLU(),
        nn.MaxPool2d(2)  # 128*56*56
        )

        self.layer2 = nn.Sequential(
            nn.Conv2d(128, 256, 3, 1, 1),  # 256*112*112
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2)  # 256*28*28
        )

        self.layer3 = nn.Sequential(
            nn.Conv2d(256, 512, 3, 1, 1),  # 256*112*112
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2)  # 512*14*14
        )

        self.pool2 = nn.MaxPool2d(2)       #512*7*7
        self.fc1 = nn.Linear(25088,1000)   #25088 --- 1000
        self.relu2 = nn.ReLU()
        self.fc2 = nn.Linear(1000,num_class)#1000 --- 11

    def forward(self,x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.pool1(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.pool2(x)
        x = x.view(x.size()[0],-1)
        x = self.fc1(x)
        x = self.relu2(x)
        x = self.fc2(x)
        return x

训练模式(和上个项目大致一样)

def train_val(model, train_loader, val_loader, no_lable_loader,device, epochs, optimizer, loss, thres,save_path):
    model = model.to(device)
    semi_loader = None #初始无半监督的数据集
    plt_train_loss = [] #记录所有轮次的loss
    plt_val_loss = []

    plt_train_acc = []  #记录所有轮次的准确度
    plt_val_acc = []

    max_acc = 0.0

    for epoch in range(epochs):   #冲锋的号角
        train_loss = 0.0
        val_loss = 0.0
        train_acc = 0.0
        val_acc = 0.0
        semi_loss = 0.0
        semi_acc = 0.0

        start_time = time.time()  #开始时间

        model.train()     #模型调为训练模式
        for batch_x, batch_y in train_loader:
            x, target = batch_x.to(device), batch_y.to(device)
            pred = model(x)
            train_bat_loss = loss(pred, target)
            train_bat_loss.backward()
            optimizer.step() #更新模型的作用
            optimizer.zero_grad()
            train_loss += train_bat_loss.cpu().item()  #本批次的loss
            train_acc += np.sum(np.argmax(pred.detach().cpu().numpy(),axis=1) == target.cpu().numpy())#每一轮预测对了多少个
        plt_train_loss.append(train_loss / train_loader.__len__())#当前轮次的loss
        plt_train_acc.append(train_acc/train_loader.dataset.__len__())#记录准确率

半监督数据存在,则启用

 if semi_loader != None:
            for batch_x, batch_y in semi_loader:
                x, target = batch_x.to(device), batch_y.to(device)
                pred = model(x)
                semi_bat_loss = loss(pred, target)
                semi_bat_loss.backward()
                optimizer.step()  # 更新模型的作用
                optimizer.zero_grad()
                semi_loss += train_bat_loss.cpu().item()
                semi_acc += np.sum(np.argmax(pred.detach().cpu().numpy(), axis=1) == target.cpu().numpy())  # 每一轮预测对了多少个
            print("半监督数据集的训练准确率为",semi_acc/train_loader.dataset.__len__())

验证模式

 model.eval()
        with torch.no_grad():
            for batch_x, batch_y in val_loader:
                x, target = batch_x.to(device), batch_y.to(device)
                pred = model(x)
                val_bat_loss = loss(pred, target)
                val_loss += val_bat_loss.cpu().item()
                val_acc += np.sum(np.argmax(pred.detach().cpu().numpy(), axis=1) == target.cpu().numpy())
        plt_val_loss.append(val_loss/ val_loader.__len__())
        plt_val_acc.append(val_acc / val_loader.dataset.__len__())

五轮一次尝试获得半监督数据作为训练集

        if epoch%5 == 0 and plt_val_acc[-1] > 0.7:#本轮模型正确率达到0.7,尝试验证  不是每一轮都尝试,太浪费时间
            semi_loader = get_semi_loader(no_lable_loader,model,device,thres)

保存最好模型

 if val_acc > max_acc:
            torch.save(model, save_path)
            max_acc = val_acc

打印信息

print("[%03d/%03d] %2.2f sec(s) Trainloss: %.6f |Valloss: %.6f Trainacc: %.6f |Valacc: %.6f"% \
              (epoch, epochs, time.time()-start_time, plt_train_loss[-1], plt_val_loss[-1],plt_train_acc[-1], plt_val_acc[-1]))

可视化图像

 plt.plot(plt_train_loss)
    plt.plot(plt_val_loss)
    plt.title("loss")
    plt.legend(["train", "val"])
    plt.show()

    plt.plot(plt_train_acc)
    plt.plot(plt_val_acc)
    plt.title("acc")
    plt.legend(["train", "val"])
    plt.show()

选择模型,可以用其他预训练模型
提取特征的网络成为特征提取器,自己设计的架构提取特征的效果很差,所以这里就需要用到别人训练好的特征提取器,其提取特征的能力很强,适合数据量很少的时候使用,此称为迁移学习。我们借用其他人网络的特征提取器加上自己的分类头即可。

#model = myModel(11)
model,_ = initialize_model("resnet18",11,use_pretrained=True)#返回两个值,我们只用一个model,可以随意调用模型,use_pretrained=True表示是否线性

from torchvision.models import resnet18

其他数据
optimizer优化器,在之前的优化器用的sgd,他会往当前梯度减小的方向更新参数,adam相比之下有两个改进,1.它会结合之前梯度方向和当前梯度方向优化参数;2.他会动态的调整学习率,当梯度过大时让学习率小一点,梯度过小时学习率大一点。adam很难出现梯度爆炸的情况。dadmw是让w权重衰减,loss+w*w(注意,这是不是正则化!)让曲线更加平滑。

lr = 0.001
loss = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(),lr=lr, weight_decay=1e-4)#sgd经常训练梯度爆炸,而Adam会动态调整参数,AdamW权重衰减,曲线更加平滑
device = "cuda" if torch.cuda.is_available() else "cpu"
save_path = "model_save/best_model.path"
epochs = 10
thres = 0.1
semi_set = semiDataset(no_lable_loader,model,device,thres=0.99)

train_val(model, train_loader, val_loader, no_lable_loader,device, epochs, optimizer, loss, thres,save_path)

完整代码

import random
import torch
import torch.nn as nn
import numpy as np
import os

from torch.utils.data import DataLoader , Dataset
from PIL import Image            #读取图片数据
from tqdm import tqdm         #可以看看读到哪里了
from torchvision import transforms   #数据增广

import time
import matplotlib.pyplot as plt

from model_utils.model import initialize_model
from model_utils.train import train_val
from model_utils.data import getDataLoader


# os.environ['CUDA_VISIBLE_DEVICES']='0,1'


def seed_everything(seed):#让所有随机都固定下来
    torch.manual_seed(seed)
    torch.cuda.manual_seed(seed)
    torch.cuda.manual_seed_all(seed)
    torch.backends.cudnn.benchmark = False
    torch.backends.cudnn.deterministic = True
    random.seed(seed)
    np.random.seed(seed)
    os.environ['PYTHONHASHSEED'] = str(seed)
#################################################################
seed_everything(0)
###############################################

HW = 224#图片尺寸改为224*224





tran_transform = transforms.Compose(
    [
        transforms.ToPILImage(),              #将图像转化为PIL图像
        transforms.RandomResizedCrop(224),    #随机放大裁剪
        transforms.RandomRotation(50),        #旋转50
        transforms.ToTensor()                 ##将224,224,3转换为模型可接受的3,224,224
    ]
)

val_transform = transforms.Compose(
    [
        transforms.ToPILImage(),
        transforms.ToTensor()
    ]
)



class food_Dataset(Dataset):
    def __init__(self,path,mode = "train"):
        self.mode = mode
        if mode == "semi":
            self.X = self.read_file(path)
        else:
            self.X,self.Y = self.read_file(path)
            self.Y = torch.LongTensor(self.Y)#将标签转化为长整型
        if mode == "train":
            self.transform = tran_transform
        else:
            self.transform = val_transform

    def read_file(self,path):  # 读文件

        if self.mode == "semi":
            file_list = os.listdir(path)  # 读出文件夹下所有文件名字
            xi = np.zeros((len(file_list), HW, HW, 3), dtype=np.uint8)  # 存图片和标签
            for j, img_name in enumerate(file_list):
                img_path = os.path.join(path, img_name)
                img = Image.open(img_path)  # 将图片读入进来
                img = img.resize((HW, HW))
                xi[j, ...] = img  # ...表示后面尺寸不变
            print("读到了%d个训练数据" % len(xi))
            return xi


        else:
            for i in tqdm(range(11)):
                file_dir = path + "/%02d" % i
                file_list = os.listdir(file_dir)  # 读出文件夹下所有文件名字

                xi = np.zeros((len(file_list), HW, HW, 3), dtype=np.uint8)  # 存图片和标签
                yi = np.zeros(len(file_list), dtype=np.uint8)

                for j, img_name in enumerate(file_list):
                    img_path = os.path.join(file_dir, img_name)
                    img = Image.open(img_path)  # 将图片读入进来
                    img = img.resize((HW, HW))
                    xi[j, ...] = img  # ...表示后面尺寸不变
                    yi[j] = i

                # 将11类图片放在一个大矩阵里
                if i == 0:  # 如果是0类,则让0类作为X,Y,其他类往后接
                    X = xi
                    Y = yi
                else:
                    X = np.concatenate((X, xi), axis=0)  # 应为要竖着接,故axis = 0
                    Y = np.concatenate((Y, yi), axis=0)
            print("读到了%d个训练数据" % len(Y))
            return X, Y

    def __getitem__(self, item):
        if self.mode == "semi":
            return self.transform(self.X[item]), self.X[item]
        else:
            return self.transform(self.X[item]),self.Y[item]


    def __len__(self):
        return len(self.X)



class semiDataset(Dataset):
    def __init__(self,no_label_loder,model,device,thres=0.99):#置信度为0.99
        x,y = self.get_label(no_label_loder,model,device,thres)
        if x == []:
            self.flag = False

        else:
            self.flag = True
            self.X = np.array(x)
            self.Y = torch.LongTensor(y)
            self.transform = tran_transform

    def get_label(self,no_label_loder,model,device,thres):
        model = model.to(device)
        pred_prob = []
        labels = []
        x = []
        y = []
        soft = nn.Softmax()

        with torch.no_grad():
            for bat_x, _ in no_label_loder:
                bat_x = bat_x.to(device)
                pred = model(bat_x)
                pred_soft = soft(pred)
                pred_max,pred_value = pred_soft.max(1)#返回最后结果中最大值和最大值的下标
                pred_prob.extend(pred_max.cpu().numpy().tolist())#pred_max转化为列表
                labels.extend(pred_value.cpu().numpy().tolist())
        for index,prob in enumerate(pred_prob):
            if  prob > thres:
                x.append(no_label_loder.dataset[index][1])  #  调用到原始的getitem
                y.append(labels[index])

        return x,y

    def __getitem__(self, item):
        return self.transform(self.X[item]),self.Y[item]
    def __len__(self):
        return len(self.X)


def get_semi_loader(no_label_loder,model,device,thres):
    semiset = semiDataset(no_label_loder,model,device,thres)
    if semiset.flag == False:#没有置信度超过0.99的数据
        return None
    else:
        semi_loader = DataLoader(semiset,batch_size=16,shuffle=False)
        return semi_loader
# val_path =r"E:\深度学习相关\第四五节_分类代码 (1)\food_classification\food-11\validation"#r是去出转义字符
# train_path =r"E:\深度学习相关\第四五节_分类代码 (1)\food_classification\food-11\training\labeled"#r是去出转义字符
no_lable_path =r"E:\深度学习相关\第四五节_分类代码 (1)\food_classification\food-11_sample\training\unlabeled\00"#r是去出转义字符
train_path =r"E:\深度学习相关\第四五节_分类代码 (1)\food_classification\food-11_sample\training\labeled"#r是去出转义字符
val_path =r"E:\深度学习相关\第四五节_分类代码 (1)\food_classification\food-11_sample\validation"#r是去出转义字符
train_set = food_Dataset(train_path,"train")
val_set = food_Dataset(val_path,"val")
no_lable_set = food_Dataset(no_lable_path,"semi")

train_loader = DataLoader(train_set,batch_size=16, shuffle=True)
val_loader = DataLoader(val_set,batch_size=16, shuffle=True)
no_lable_loader = DataLoader(no_lable_set,batch_size=16, shuffle=False)#这里一定不要打乱


class myModel(nn.Module):
    def __init__(self,num_class):
        super(myModel, self).__init__()
        #3 * 224 *224   ->   512*512   ->   拉直   ->   全连接
        self.conv1 = nn.Conv2d(3,64,3,1,1)         #64*224*224
        self.bn1 = nn.BatchNorm2d(64)
        self.relu = nn.ReLU()
        self.pool1 = nn.MaxPool2d(2)       #64*112*112

        self.layer1 = nn.Sequential(
        nn.Conv2d(64, 128, 3, 1, 1)  ,# 128*112*112
        nn.BatchNorm2d(128),
        nn.ReLU(),
        nn.MaxPool2d(2)  # 128*56*56
        )

        self.layer2 = nn.Sequential(
            nn.Conv2d(128, 256, 3, 1, 1),  # 256*112*112
            nn.BatchNorm2d(256),
            nn.ReLU(),
            nn.MaxPool2d(2)  # 256*28*28
        )

        self.layer3 = nn.Sequential(
            nn.Conv2d(256, 512, 3, 1, 1),  # 256*112*112
            nn.BatchNorm2d(512),
            nn.ReLU(),
            nn.MaxPool2d(2)  # 512*14*14
        )

        self.pool2 = nn.MaxPool2d(2)       #512*7*7
        self.fc1 = nn.Linear(25088,1000)   #25088 --- 1000
        self.relu2 = nn.ReLU()
        self.fc2 = nn.Linear(1000,num_class)#1000 --- 11

    def forward(self,x):
        x = self.conv1(x)
        x = self.bn1(x)
        x = self.relu(x)
        x = self.pool1(x)
        x = self.layer1(x)
        x = self.layer2(x)
        x = self.layer3(x)
        x = self.pool2(x)
        x = x.view(x.size()[0],-1)
        x = self.fc1(x)
        x = self.relu2(x)
        x = self.fc2(x)
        return x

def train_val(model, train_loader, val_loader, no_lable_loader,device, epochs, optimizer, loss, thres,save_path):
    model = model.to(device)
    semi_loader = None
    plt_train_loss = [] #记录所有轮次的loss
    plt_val_loss = []

    plt_train_acc = []
    plt_val_acc = []

    max_acc = 0.0

    for epoch in range(epochs):   #冲锋的号角
        train_loss = 0.0
        val_loss = 0.0
        train_acc = 0.0
        val_acc = 0.0
        semi_loss = 0.0
        semi_acc = 0.0

        start_time = time.time()

        model.train()     #模型调为训练模式
        for batch_x, batch_y in train_loader:
            x, target = batch_x.to(device), batch_y.to(device)
            pred = model(x)
            train_bat_loss = loss(pred, target)
            train_bat_loss.backward()
            optimizer.step() #更新模型的作用
            optimizer.zero_grad()
            train_loss += train_bat_loss.cpu().item()
            train_acc += np.sum(np.argmax(pred.detach().cpu().numpy(),axis=1) == target.cpu().numpy())#每一轮预测对了多少个
        plt_train_loss.append(train_loss / train_loader.__len__())
        plt_train_acc.append(train_acc/train_loader.dataset.__len__())#记录准确率

        if semi_loader != None:
            for batch_x, batch_y in semi_loader:
                x, target = batch_x.to(device), batch_y.to(device)
                pred = model(x)
                semi_bat_loss = loss(pred, target)
                semi_bat_loss.backward()
                optimizer.step()  # 更新模型的作用
                optimizer.zero_grad()
                semi_loss += train_bat_loss.cpu().item()
                semi_acc += np.sum(np.argmax(pred.detach().cpu().numpy(), axis=1) == target.cpu().numpy())  # 每一轮预测对了多少个
            print("半监督数据集的训练准确率为",semi_acc/train_loader.dataset.__len__())

        model.eval()
        with torch.no_grad():
            for batch_x, batch_y in val_loader:
                x, target = batch_x.to(device), batch_y.to(device)
                pred = model(x)
                val_bat_loss = loss(pred, target)
                val_loss += val_bat_loss.cpu().item()
                val_acc += np.sum(np.argmax(pred.detach().cpu().numpy(), axis=1) == target.cpu().numpy())
        plt_val_loss.append(val_loss/ val_loader.__len__())
        plt_val_acc.append(val_acc / val_loader.dataset.__len__())

        if epoch%5 == 0 and plt_val_acc[-1] > 0.7:#本轮模型正确率达到0.7,尝试验证  不是每一轮都尝试,太浪费时间
            semi_loader = get_semi_loader(no_lable_loader,model,device,thres)

        if val_acc > max_acc:
            torch.save(model, save_path)
            max_acc = val_acc

        print("[%03d/%03d] %2.2f sec(s) Trainloss: %.6f |Valloss: %.6f Trainacc: %.6f |Valacc: %.6f"% \
              (epoch, epochs, time.time()-start_time, plt_train_loss[-1], plt_val_loss[-1],plt_train_acc[-1], plt_val_acc[-1]))


    plt.plot(plt_train_loss)
    plt.plot(plt_val_loss)
    plt.title("loss")
    plt.legend(["train", "val"])
    plt.show()

    plt.plot(plt_train_acc)
    plt.plot(plt_val_acc)
    plt.title("acc")
    plt.legend(["train", "val"])
    plt.show()

#model = myModel(11)
model,_ = initialize_model("resnet18",11,use_pretrained=True)#返回两个值,我们只用一个model,可以随意调用模型,use_pretrained=True表示是否线性

from torchvision.models import resnet18


lr = 0.001
loss = nn.CrossEntropyLoss()
optimizer = torch.optim.AdamW(model.parameters(),lr=lr, weight_decay=1e-4)#sgd经常训练梯度爆炸,而Adam会动态调整参数,AdamW权重衰减,曲线更加平滑
device = "cuda" if torch.cuda.is_available() else "cpu"
save_path = "model_save/best_model.path"
epochs = 10
thres = 0.1
semi_set = semiDataset(no_lable_loader,model,device,thres=0.99)

train_val(model, train_loader, val_loader, no_lable_loader,device, epochs, optimizer, loss, thres,save_path)










运行结果展示
在这里插入图片描述
在这里插入图片描述

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐