半监督用已有模型对数据打标签然后分离出可用的数据用于模型训练因为模型训练的参数是随机的为了确保每次都可以得到最好的模型参数所以我们要固定种子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) ###############################################为了避免模型只认识一种图片转一下局部就不认识了这里对图片进行一些处理这样可以让模型认识更多的图片train_transform transforms.Compose( [ transforms.ToPILImage(), transforms.RandomResizedCrop(224), transforms.RandomRotation(50), transforms.ToTensor() ] ) val_transform transforms.Compose( [ transforms.ToPILImage(), transforms.ToTensor() ] )数据类class food_Dataset(Dataset): def __init__(self, path, modetrain):#传入模式用于区分数据用途 self.mode mode #让类内其他函数也可以使用mode if mode semi:#半监督数据处理 self.X self.read_file(path)#因为半监督的数据集是没有标签的所以这里只承接x else:#其他情况有标签所以承接xy self.X, self.Y self.read_file(path) self.Y torch.LongTensor(self.Y) if mode train:#训练集 self.transform train_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), dtypenp.uint8) #创建一个“容器”来存放这些图片数量宽高通道数输配像素范围 for idx, img_name in enumerate(tqdm(file_list)):#tqdm加进度条enumerate加序号返回序号文件名 img_path os.path.join(path, img_name)#拼接文件路径 img Image.open(img_path)#打开文件 img img.resize((HW, HW))#强制文件是hw*hw形状的 xi[idx, ...] img#存入xi数组 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), dtypenp.uint8)#定义容器 yi np.zeros(len(file_list), dtypenp.uint8) for idx, img_name in enumerate(tqdm(file_list)):#加序号进度条 img_path os.path.join(file_dir, img_name)#拼接路径 img Image.open(img_path)#打开文件 img img.resize((HW, HW))#定义大小 xi[idx, ...] img#存入文件 yi[idx] i#存入标签 if i 0:#只有一个文件所以不需要拼接 X xi Y yi else: X np.concatenate((X, xi), axis0)#文件拼起来0纵向1横向 Y np.concatenate((Y, yi), axis0) print(读到了%d个数据 % len(Y)) return X, Ydef __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)#返回长度调用train_loader DataLoader(train_set, batch_size4, shuffleTrue) #数据每次几个是否打乱 #注意半监督集不能打乱半监督数据类class semiDataset(Dataset): def __init__(self, no_label_loder, model, device, thres0.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.transfrom train_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())#把得到的数据转换为列表 labels.extend(labels.cpu().numpy().tolist()) for index, prob in enumerate(pred_prob): if prob thres:#如果大于置信度阈值则加入训练集 x.append(no_label_loder, Dataset[index][1]) y.append(labels[index]) return x, y def __getitem__(self, item): return self.transfrom(self.X[item]), self.Y[item] def __len__(self): return len(self.X)处理半监督文件因为半监督的数据不会一直有所以要判断一下def get_semi_loder(no_label_loder, model, device, thres): semiset semiDataset(no_label_loder, model, device, thres) if semiset.flag False: return None else: semi_loder DataLoader(semiset,batch_size16,shuffleFalse) return semi_loder模型菜鸟class myModel(nn.Module): def __init__(self, num_class): super(myModel, self).__init__() self.layer0 nn.Sequential( nn.Conv2d(3, 64, 3, 1, 1), # 64*224*224 卷积 nn.BatchNorm2d(64),#归一化 nn.ReLU(),#激活函数 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*56*56 nn.BatchNorm2d(256), nn.ReLU(), nn.MaxPool2d(2) # 256*28*28 ) self.layer3 nn.Sequential( nn.Conv2d(256, 512, 3, 1, 1), # 512*28*28 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) self.relu2 nn.ReLU() self.fc2 nn.Linear(1000, num_class) def forward(self, x): x self.layer0(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_label_loader, device, epochs, optimizer, loss, thres, save_path): #模型训练集验证集无监督集设备轮数优化器loss置信度阈值模型存储地址 model model.to(device)#模型放到设备上 semi_loader None plt_train_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)#求loss 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(), axis1) 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(), axis1) 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(), axis1) target.cpu().numpy()) plt_val_loss.append(val_loss / val_loader.dataset.__len__()) plt_val_acc.append(val_acc / val_loader.dataset.__len__()) if epoch % 3 0 and plt_val_acc[-1] 0.6:#每三轮且准确率大于0.6处理一批半监督数据 semi_loader get_semi_loader(no_label_loader, model, device, thres) if val_acc max_acc:#如果准确率历史最高则记录模型 torch.save(model, save_path) max_acc val_loss 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]) ) # 打印训练结果。 注意python语法 %2.2f 表示小数位为2的浮点数 后面可以对应。 #画图 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_pretrainedTrue) 模型名称类别总数是否使用参数model_ft models.resnet18(pretraineduse_pretrained) # 从网络下载模型 pretrain true 使用参数和架构 false 仅使用架构。 set_parameter_requires_grad(model_ft, linear_prob) # 是否为线性探测线性探测 固定特征提取器不训练。 num_ftrs model_ft.fc.in_features #分类头的输入维度 model_ft.fc nn.Linear(num_ftrs, num_classes) # 删掉原来分类头 更改最后一层为想要的分类数的分类头。 input_size 224
基于迁移学习,半监督学习的图片识别
半监督用已有模型对数据打标签然后分离出可用的数据用于模型训练因为模型训练的参数是随机的为了确保每次都可以得到最好的模型参数所以我们要固定种子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) ###############################################为了避免模型只认识一种图片转一下局部就不认识了这里对图片进行一些处理这样可以让模型认识更多的图片train_transform transforms.Compose( [ transforms.ToPILImage(), transforms.RandomResizedCrop(224), transforms.RandomRotation(50), transforms.ToTensor() ] ) val_transform transforms.Compose( [ transforms.ToPILImage(), transforms.ToTensor() ] )数据类class food_Dataset(Dataset): def __init__(self, path, modetrain):#传入模式用于区分数据用途 self.mode mode #让类内其他函数也可以使用mode if mode semi:#半监督数据处理 self.X self.read_file(path)#因为半监督的数据集是没有标签的所以这里只承接x else:#其他情况有标签所以承接xy self.X, self.Y self.read_file(path) self.Y torch.LongTensor(self.Y) if mode train:#训练集 self.transform train_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), dtypenp.uint8) #创建一个“容器”来存放这些图片数量宽高通道数输配像素范围 for idx, img_name in enumerate(tqdm(file_list)):#tqdm加进度条enumerate加序号返回序号文件名 img_path os.path.join(path, img_name)#拼接文件路径 img Image.open(img_path)#打开文件 img img.resize((HW, HW))#强制文件是hw*hw形状的 xi[idx, ...] img#存入xi数组 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), dtypenp.uint8)#定义容器 yi np.zeros(len(file_list), dtypenp.uint8) for idx, img_name in enumerate(tqdm(file_list)):#加序号进度条 img_path os.path.join(file_dir, img_name)#拼接路径 img Image.open(img_path)#打开文件 img img.resize((HW, HW))#定义大小 xi[idx, ...] img#存入文件 yi[idx] i#存入标签 if i 0:#只有一个文件所以不需要拼接 X xi Y yi else: X np.concatenate((X, xi), axis0)#文件拼起来0纵向1横向 Y np.concatenate((Y, yi), axis0) print(读到了%d个数据 % len(Y)) return X, Ydef __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)#返回长度调用train_loader DataLoader(train_set, batch_size4, shuffleTrue) #数据每次几个是否打乱 #注意半监督集不能打乱半监督数据类class semiDataset(Dataset): def __init__(self, no_label_loder, model, device, thres0.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.transfrom train_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())#把得到的数据转换为列表 labels.extend(labels.cpu().numpy().tolist()) for index, prob in enumerate(pred_prob): if prob thres:#如果大于置信度阈值则加入训练集 x.append(no_label_loder, Dataset[index][1]) y.append(labels[index]) return x, y def __getitem__(self, item): return self.transfrom(self.X[item]), self.Y[item] def __len__(self): return len(self.X)处理半监督文件因为半监督的数据不会一直有所以要判断一下def get_semi_loder(no_label_loder, model, device, thres): semiset semiDataset(no_label_loder, model, device, thres) if semiset.flag False: return None else: semi_loder DataLoader(semiset,batch_size16,shuffleFalse) return semi_loder模型菜鸟class myModel(nn.Module): def __init__(self, num_class): super(myModel, self).__init__() self.layer0 nn.Sequential( nn.Conv2d(3, 64, 3, 1, 1), # 64*224*224 卷积 nn.BatchNorm2d(64),#归一化 nn.ReLU(),#激活函数 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*56*56 nn.BatchNorm2d(256), nn.ReLU(), nn.MaxPool2d(2) # 256*28*28 ) self.layer3 nn.Sequential( nn.Conv2d(256, 512, 3, 1, 1), # 512*28*28 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) self.relu2 nn.ReLU() self.fc2 nn.Linear(1000, num_class) def forward(self, x): x self.layer0(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_label_loader, device, epochs, optimizer, loss, thres, save_path): #模型训练集验证集无监督集设备轮数优化器loss置信度阈值模型存储地址 model model.to(device)#模型放到设备上 semi_loader None plt_train_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)#求loss 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(), axis1) 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(), axis1) 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(), axis1) target.cpu().numpy()) plt_val_loss.append(val_loss / val_loader.dataset.__len__()) plt_val_acc.append(val_acc / val_loader.dataset.__len__()) if epoch % 3 0 and plt_val_acc[-1] 0.6:#每三轮且准确率大于0.6处理一批半监督数据 semi_loader get_semi_loader(no_label_loader, model, device, thres) if val_acc max_acc:#如果准确率历史最高则记录模型 torch.save(model, save_path) max_acc val_loss 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]) ) # 打印训练结果。 注意python语法 %2.2f 表示小数位为2的浮点数 后面可以对应。 #画图 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_pretrainedTrue) 模型名称类别总数是否使用参数model_ft models.resnet18(pretraineduse_pretrained) # 从网络下载模型 pretrain true 使用参数和架构 false 仅使用架构。 set_parameter_requires_grad(model_ft, linear_prob) # 是否为线性探测线性探测 固定特征提取器不训练。 num_ftrs model_ft.fc.in_features #分类头的输入维度 model_ft.fc nn.Linear(num_ftrs, num_classes) # 删掉原来分类头 更改最后一层为想要的分类数的分类头。 input_size 224