日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問(wèn) 生活随笔!

生活随笔

當(dāng)前位置: 首頁(yè) > 人工智能 > ChatGpt >内容正文

ChatGpt

AI入门:Transfer Learning(迁移学习)

發(fā)布時(shí)間:2025/3/8 ChatGpt 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 AI入门:Transfer Learning(迁移学习) 小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,幫大家做個(gè)參考.

遷移學(xué)習(xí)是一種機(jī)器學(xué)習(xí)方法,就是把為任務(wù) A 開(kāi)發(fā)的模型作為初始點(diǎn),重新使用在為任務(wù) B 開(kāi)發(fā)模型的過(guò)程中

Pokemon Dataset

通過(guò)網(wǎng)絡(luò)上收集寶可夢(mèng)的圖片,制作圖像分類數(shù)據(jù)集。我收集了5種寶可夢(mèng),分別是皮卡丘,超夢(mèng),杰尼龜,小火龍,妙蛙種子

數(shù)據(jù)集鏈接:https://pan.baidu.com/s/1Kept7FF88lb8TqPZMD_Yxw提取碼:1sdd

一共有1168張寶可夢(mèng)的圖片,其中皮卡丘234張,超夢(mèng)239張,杰尼龜223張,小火龍238張,妙蛙種子234張

每個(gè)目錄由神奇寶貝名字命名,對(duì)應(yīng)目錄下是該神奇寶貝的圖片,圖片的格式有jpg,png,jpeg三種

數(shù)據(jù)集的劃分如下(訓(xùn)練集60%,驗(yàn)證集20%,測(cè)試集20%)。這個(gè)比例不是針對(duì)每一類提取,而是針對(duì)總體的1168張

Load Data

在PyTorch中定義數(shù)據(jù)集主要涉及到兩個(gè)主要的類:Dataset和DataLoder

DataSet類

DataSet類是PyTorch中所有數(shù)據(jù)集加載類中都應(yīng)該繼承的父類,它的兩個(gè)私有成員函數(shù)__len__()和__getitem__()必須被重載,否則將觸發(fā)錯(cuò)誤提示

其中__len__()應(yīng)該返回?cái)?shù)據(jù)集的樣本數(shù)量,而__getitem__()實(shí)現(xiàn)通過(guò)索引返回樣本數(shù)據(jù)的功能

首先看一個(gè)自定義Dataset的例子

class NumbersDataset(Dataset):def __init__(self, training=True):if training:self.samples = list(range(1, 1001))else:self.samples = list(range(1001, 1501))def __len__(self):return len(self.samples)def __getitem__(self, idx):return self.samples[idx]

然后需要對(duì)圖片做Preprocessing

  • Image Resize:224*224 for ResNet18

  • Data Argumentation:Rotate & Crop

  • Normalize:Mean & std

  • ToTensor

  • 首先我們?cè)赺_init__()函數(shù)里將name->label,這里的name就是文件夾的名字,然后拆分?jǐn)?shù)據(jù)集,按照6:2:2的比例

    class Pokemon(Dataset):def __init__(self, root, resize, model):super(Pokemon, self).__init__()self.root = rootself.resize = resizeself.name2label = {} # 將文件夾的名字映射為label(數(shù)字)for name in sorted(os.listdir(os.path.join(root))):if not os.path.isdir(os.path.join(root, name)):continueself.name2label[name] = len(self.name2label.keys())# image, labelself.images, self.labels = self.load_csv('images.csv')if model == 'train': # 60%self.images = self.images[:int(0.6*len(self.images))]self.labels = self.labels[:int(0.6*len(self.labels))]elif model == 'val': # 20%self.images = self.images[int(0.6*len(self.images)):int(0.8*len(self.images))]self.labels = self.labels[int(0.6*len(self.labels)):int(0.8*len(self.labels))]else: # 20%self.images = self.images[int(0.8*len(self.images)):]self.labels = self.labels[int(0.8*len(self.labels)):]

    其中l(wèi)oad_csv()函數(shù)的作用是將所有的圖片名(名字里包含完整的路徑)以及l(fā)abel都存到csv文件里,例如,有一個(gè)圖片的路徑是pokemon\\bulbasaur\\00000000.png,對(duì)應(yīng)的label是0,那么csv就會(huì)寫(xiě)入一行pokemon\\bulbasaur\\00000000.png, 0,總共寫(xiě)入了1167行(有一張圖片既不是png,也不是jpg和jpeg,找不到,算了)。load_csv()函數(shù)具體如下所示

    def load_csv(self, filename):if not os.path.exists(os.path.join(self.root, filename)):images = []for name in self.name2label.keys():images += glob.glob(os.path.join(self.root, name, '*.png'))images += glob.glob(os.path.join(self.root, name, '*.jpg'))images += glob.glob(os.path.join(self.root, name, '*.jpeg'))random.shuffle(images)with open(os.path.join(self.root, filename), mode='w', newline='') as f:writer = csv.writer(f)for img in images: # pokemon\\bulbasaur\\00000000.pngname = img.split(os.sep)[-2] # bulbasaurlabel = self.name2label[name]# pokemon\\bulbasaur\\00000000.png 0writer.writerow([img, label])print('writen into csv file:', filename)# read csv fileimages, labels = [], []with open(os.path.join(self.root, filename)) as f:reader = csv.reader(f)for row in reader:image, label = rowlabel = int(label)images.append(image)labels.append(label)assert len(images) == len(labels)return images, labels

    然后是__len__()函數(shù)的代碼

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

    最后是__getitem__()函數(shù)的代碼,這個(gè)比較復(fù)雜,因?yàn)槲覀儸F(xiàn)在只有圖片的string path(字符串形式的路徑),要先轉(zhuǎn)成三通道的image data,這個(gè)利用PIL庫(kù)中的Image.open(path).convert('RGB')函數(shù)可以完成。圖片讀取出來(lái)以后,要經(jīng)過(guò)一系列的transforms,具體代碼如下

    def __getitem__(self, idx):# idx [0~len(images)]# self.images, self.labels# pokemon\\bulbasaur\\00000000.png 0img, label = self.images[idx], self.labels[idx]tf = transforms.Compose([lambda x:Image.open(x).convert('RGB'), # string path => image datatransforms.Resize((int(self.resize*1.25), int(self.resize*1.25))),transforms.RandomRotation(15),transforms.CenterCrop(self.resize),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])img = tf(img)label = torch.tensor(label)return img, label

    Normalize的參數(shù)是PyTorch推薦的,直接寫(xiě)上就可以了

    DataLoader類

    Dataset類是讀入數(shù)據(jù)集并對(duì)讀入的數(shù)據(jù)進(jìn)行了索引,但是光有這個(gè)功能是不夠的,在實(shí)際加載數(shù)據(jù)集的過(guò)程中,我們的數(shù)據(jù)量往往都很大,因此還需要以下幾個(gè)功能:

  • 每次讀入一些批次:batch_size

  • 可以對(duì)數(shù)據(jù)進(jìn)行隨機(jī)讀取,打亂數(shù)據(jù)的順序(shuffling)

  • 可以并行加載數(shù)據(jù)集(利用多核處理器加快載入數(shù)據(jù)的效率)

  • 為此,就需要DataLoader類了,它里面常用的參數(shù)有:

    • batch_size:每個(gè)batch的大小

    • shuffle:是否進(jìn)行shuffle操作

    • num_works:加載數(shù)據(jù)的時(shí)候使用幾個(gè)進(jìn)程

    DataLoader這個(gè)類并不需要我們自己設(shè)計(jì)代碼,只需要利用它讀取我們?cè)O(shè)計(jì)好的Dataset的子類即可

    db = Pokemon('pokemon', 224, 'train') lodder = DataLoader(db, batch_size=32, shuffle=True, num_workers=4)

    完整代碼如下:

    import torch import os, glob import random, csv from torch.utils.data import Dataset, DataLoader from torchvision import transforms from PIL import Imageclass Pokemon(Dataset):def __init__(self, root, resize, model):super(Pokemon, self).__init__()self.root = rootself.resize = resizeself.name2label = {} # 將文件夾的名字映射為label(數(shù)字)for name in sorted(os.listdir(os.path.join(root))):if not os.path.isdir(os.path.join(root, name)):continueself.name2label[name] = len(self.name2label.keys())# image, labelself.images, self.labels = self.load_csv('images.csv')if model == 'train': # 60%self.images = self.images[:int(0.6*len(self.images))]self.labels = self.labels[:int(0.6*len(self.labels))]elif model == 'val': # 20%self.images = self.images[int(0.6*len(self.images)):int(0.8*len(self.images))]self.labels = self.labels[int(0.6*len(self.labels)):int(0.8*len(self.labels))]else: # 20%self.images = self.images[int(0.8*len(self.images)):]self.labels = self.labels[int(0.8*len(self.labels)):]def load_csv(self, filename):if not os.path.exists(os.path.join(self.root, filename)):images = []for name in self.name2label.keys():images += glob.glob(os.path.join(self.root, name, '*.png'))images += glob.glob(os.path.join(self.root, name, '*.jpg'))images += glob.glob(os.path.join(self.root, name, '*.jpeg'))random.shuffle(images)with open(os.path.join(self.root, filename), mode='w', newline='') as f:writer = csv.writer(f)for img in images: # pokemon\\bulbasaur\\00000000.pngname = img.split(os.sep)[-2] # bulbasaurlabel = self.name2label[name]# pokemon\\bulbasaur\\00000000.png 0writer.writerow([img, label])print('writen into csv file:', filename)# read csv fileimages, labels = [], []with open(os.path.join(self.root, filename)) as f:reader = csv.reader(f)for row in reader:image, label = rowlabel = int(label)images.append(image)labels.append(label)assert len(images) == len(labels)return images, labelsdef __len__(self):return len(self.images)def __getitem__(self, idx):# idx [0~len(images)]# self.images, self.labels# pokemon\\bulbasaur\\00000000.png 0img, label = self.images[idx], self.labels[idx]tf = transforms.Compose([lambda x:Image.open(x).convert('RGB'), # string path => image datatransforms.Resize((int(self.resize*1.25), int(self.resize*1.25))),transforms.RandomRotation(15),transforms.CenterCrop(self.resize),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])img = tf(img)label = torch.tensor(label)return img, labeldb = Pokemon('pokemon', 224, 'train') lodder = DataLoader(db, batch_size=32, shuffle=True, num_workers=8)

    Build Model

    用PyTorch搭建ResNet其實(shí)在我之前的文章(https://wmathor.com/index.php/archives/1389/)已經(jīng)講過(guò)了,這里直接拿來(lái)用,修改一下里面的參數(shù)就行了

    import torch import torch.nn as nn import torch.nn.functional as Fclass ResBlk(nn.Module):def __init__(self, ch_in, ch_out, stride=1):super(ResBlk, self).__init__()self.conv1 = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=stride, padding=1)self.bn1 = nn.BatchNorm2d(ch_out)self.conv2 = nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1)self.bn2 = nn.BatchNorm2d(ch_out)self.extra = nn.Sequential()if ch_out != ch_in:self.extra = nn.Sequential(nn.Conv2d(ch_in, ch_out, kernel_size=1, stride=stride),nn.BatchNorm2d(ch_out),)def forward(self, x):out = F.relu(self.bn1(self.conv1(x)))out = self.bn2(self.conv2(out))# short cutout = self.extra(x) + outout = F.relu(out)return outclass ResNet18(nn.Module):def __init__(self, num_class):super(ResNet18, self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(3, 16, kernel_size=3, stride=3, padding=0),nn.BatchNorm2d(16),)# followed 4 blocks# [b, 16, h, w] => [b, 32, h, w]self.blk1 = ResBlk(16, 32, stride=3)# [b, 32, h, w] => [b, 64, h, w]self.blk2 = ResBlk(32, 64, stride=3)# [b, 64, h, w] => [b, 128, h, w]self.blk3 = ResBlk(64, 128, stride=2)# [b, 128, h, w] => [b, 256, h, w]self.blk4 = ResBlk(128, 256, stride=2)self.outlayer = nn.Linear(256*3*3, num_class)def forward(self, x):x = F.relu(self.conv1(x))x = self.blk1(x)x = self.blk2(x)x = self.blk3(x)x = self.blk4(x)x = x.view(x.size(0), -1)x = self.outlayer(x)return x

    Train and Test

    訓(xùn)練的時(shí)候,嚴(yán)格按照Training和Test的邏輯,就是在訓(xùn)練epoch的過(guò)程中,間斷的做一次validation,然后看一下當(dāng)前的validation accuracy是不是最高的,如果是最高的,就把當(dāng)前的模型參數(shù)保存起來(lái)。training完以后,加載最好的模型,再做testing。這就是非常嚴(yán)格的訓(xùn)練邏輯。代碼如下:

    batchsz = 32 lr = 1e-3 epochs = 10 device = torch.device('cuda') torch.manual_seed(1234)train_db = Pokemon('pokemon', 224, model='train') val_db = Pokemon('pokemon', 224, model='val') test_db = Pokemon('pokemon', 224, model='test') train_loader = DataLoader(train_db, batch_size=batchsz, shuffle=True, num_workers=2) val_loader = DataLoader(val_db, batch_size=batchsz, num_workers=2) test_loader = DataLoader(test_db, batch_size=batchsz, num_workers=2)def evalute(model, loader):correct = 0total = len(loader.dataset)for x,y in loader:with torch.no_grad():logits = model(x)pred = logits.argmax(dim=1)correct += torch.eq(pred, y).sum().float().item()return correct / totaldef main():model = ResNet18(5)optimizer = optim.Adam(model.parameters(), lr=lr)criteon = nn.CrossEntropyLoss()best_acc, best_epoch = 0, 0for epoch in range(epochs):for step, (x, y) in enumerate(train_loader):# x:[b, 3, 224, 224], y:[b]logits = model(x)loss = criteon(logits, y)optimizer.zero_grad()loss.backward()optimizer.step()if epoch % 2 == 0:val_acc = evalute(model, val_loader)if val_acc > best_acc:best_epoch = epochbest_acc = val_acctorch.save(model.state_dict(), 'best.mdl')print('best acc:', best_acc, 'best_epoch', best_epoch)model.load_state_dict(torch.load('best.mdl'))print('loaded from ckt!')test_acc = evalute(model, test_loader)print('test_acc:', test_acc)

    截至到目前為止,能完整運(yùn)行的代碼如下:

    import torch import os, glob import warnings import random, csv from PIL import Image from torch import optim, nn import torch.nn.functional as F from torchvision import transforms from torch.utils.data import Dataset, DataLoader warnings.filterwarnings('ignore')class Pokemon(Dataset):def __init__(self, root, resize, model):super(Pokemon, self).__init__()self.root = rootself.resize = resizeself.name2label = {} # 將文件夾的名字映射為label(數(shù)字)for name in sorted(os.listdir(os.path.join(root))):if not os.path.isdir(os.path.join(root, name)):continueself.name2label[name] = len(self.name2label.keys())# image, labelself.images, self.labels = self.load_csv('images.csv')if model == 'train': # 60%self.images = self.images[:int(0.6*len(self.images))]self.labels = self.labels[:int(0.6*len(self.labels))]elif model == 'val': # 20%self.images = self.images[int(0.6*len(self.images)):int(0.8*len(self.images))]self.labels = self.labels[int(0.6*len(self.labels)):int(0.8*len(self.labels))]else: # 20%self.images = self.images[int(0.8*len(self.images)):]self.labels = self.labels[int(0.8*len(self.labels)):]def load_csv(self, filename):if not os.path.exists(os.path.join(self.root, filename)):images = []for name in self.name2label.keys():images += glob.glob(os.path.join(self.root, name, '*.png'))images += glob.glob(os.path.join(self.root, name, '*.jpg'))images += glob.glob(os.path.join(self.root, name, '*.jpeg'))random.shuffle(images)with open(os.path.join(self.root, filename), mode='w', newline='') as f:writer = csv.writer(f)for img in images: # pokemon\\bulbasaur\\00000000.pngname = img.split(os.sep)[-2] # bulbasaurlabel = self.name2label[name]# pokemon\\bulbasaur\\00000000.png 0writer.writerow([img, label])print('writen into csv file:', filename)# read csv fileimages, labels = [], []with open(os.path.join(self.root, filename)) as f:reader = csv.reader(f)for row in reader:image, label = rowlabel = int(label)images.append(image)labels.append(label)assert len(images) == len(labels)return images, labelsdef __len__(self):return len(self.images)def __getitem__(self, idx):# idx [0~len(images)]# self.images, self.labels# pokemon\\bulbasaur\\00000000.png 0img, label = self.images[idx], self.labels[idx]tf = transforms.Compose([lambda x:Image.open(x).convert('RGB'), # string path => image datatransforms.Resize((int(self.resize*1.25), int(self.resize*1.25))),transforms.RandomRotation(15),transforms.CenterCrop(self.resize),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])img = tf(img)label = torch.tensor(label)return img, labelclass ResBlk(nn.Module):def __init__(self, ch_in, ch_out, stride=1):super(ResBlk, self).__init__()self.conv1 = nn.Conv2d(ch_in, ch_out, kernel_size=3, stride=stride, padding=1)self.bn1 = nn.BatchNorm2d(ch_out)self.conv2 = nn.Conv2d(ch_out, ch_out, kernel_size=3, stride=1, padding=1)self.bn2 = nn.BatchNorm2d(ch_out)self.extra = nn.Sequential()if ch_out != ch_in:self.extra = nn.Sequential(nn.Conv2d(ch_in, ch_out, kernel_size=1, stride=stride),nn.BatchNorm2d(ch_out),)def forward(self, x):out = F.relu(self.bn1(self.conv1(x)))out = self.bn2(self.conv2(out))# short cutout = self.extra(x) + outout = F.relu(out)return outclass ResNet18(nn.Module):def __init__(self, num_class):super(ResNet18, self).__init__()self.conv1 = nn.Sequential(nn.Conv2d(3, 16, kernel_size=3, stride=3, padding=0),nn.BatchNorm2d(16),)# followed 4 blocks# [b, 16, h, w] => [b, 32, h, w]self.blk1 = ResBlk(16, 32, stride=3)# [b, 32, h, w] => [b, 64, h, w]self.blk2 = ResBlk(32, 64, stride=3)# [b, 64, h, w] => [b, 128, h, w]self.blk3 = ResBlk(64, 128, stride=2)# [b, 128, h, w] => [b, 256, h, w]self.blk4 = ResBlk(128, 256, stride=2)self.outlayer = nn.Linear(256*3*3, num_class)def forward(self, x):x = F.relu(self.conv1(x))x = self.blk1(x)x = self.blk2(x)x = self.blk3(x)x = self.blk4(x)x = x.view(x.size(0), -1)x = self.outlayer(x)return xbatchsz = 32 lr = 1e-3 epochs = 10 device = torch.device('cuda') torch.manual_seed(1234)train_db = Pokemon('pokemon', 224, model='train') val_db = Pokemon('pokemon', 224, model='val') test_db = Pokemon('pokemon', 224, model='test') train_loader = DataLoader(train_db, batch_size=batchsz, shuffle=True, num_workers=2) val_loader = DataLoader(val_db, batch_size=batchsz, num_workers=2) test_loader = DataLoader(test_db, batch_size=batchsz, num_workers=2)def evalute(model, loader):correct = 0total = len(loader.dataset)for x,y in loader:with torch.no_grad():logits = model(x)pred = logits.argmax(dim=1)correct += torch.eq(pred, y).sum().float().item()return correct / totaldef main():model = ResNet18(5)optimizer = optim.Adam(model.parameters(), lr=lr)criteon = nn.CrossEntropyLoss()best_acc, best_epoch = 0, 0for epoch in range(epochs):for step, (x, y) in enumerate(train_loader):# x:[b, 3, 224, 224], y:[b]logits = model(x)loss = criteon(logits, y)optimizer.zero_grad()loss.backward()optimizer.step()if epoch % 2 == 0:val_acc = evalute(model, val_loader)if val_acc > best_acc:best_epoch = epochbest_acc = val_acctorch.save(model.state_dict(), 'best.mdl')print('best acc:', best_acc, 'best_epoch', best_epoch)model.load_state_dict(torch.load('best.mdl'))print('loaded from ckt!')test_acc = evalute(model, test_loader)print('test_acc:', test_acc)if __name__ == '__main__':main()

    Transfer Learning

    運(yùn)行上面的代碼,基本上最終test accuracy可以達(dá)到0.88左右。如果想要提升的話,就需要使用更多工程上的tricks或者調(diào)參

    當(dāng)然還有一種方法,就是遷移學(xué)習(xí),我們先看下面這張圖,這張圖展示的問(wèn)題在于,當(dāng)數(shù)據(jù)很少的情況下(第一張圖),模型訓(xùn)練的結(jié)果可能會(huì)有很多情況(第二張圖),當(dāng)然最終輸出就一個(gè)結(jié)果。然而這個(gè)結(jié)果可能test accuracy并不高。就比方說(shuō)我們的pokemon圖片,只有1000多張,算是一個(gè)比較少的數(shù)據(jù)集了,但是由于pokemon和ImageNet都是圖片,它們可能存在某些共性。那我們能不能用ImageNet的一些train好的模型,拿來(lái)幫助我們解決一下特定的圖片分類任務(wù),這就是Transfer Learning,也就是在A任務(wù)上train好一個(gè)分類器,再transfer到B上去

    我個(gè)人理解Transfer Learning的作用是這樣的,我們都知道神經(jīng)網(wǎng)絡(luò)初始化參數(shù)非常重要,有時(shí)候初始化不好,可能就會(huì)導(dǎo)致最終效果非常差。現(xiàn)在我們用一個(gè)在A任務(wù)上已經(jīng)訓(xùn)練好了的網(wǎng)絡(luò),相當(dāng)于幫你做了一個(gè)很好的初始化,你在這個(gè)網(wǎng)絡(luò)的基礎(chǔ)上,去做B任務(wù),如果這兩個(gè)任務(wù)比較接近的話,夸張一點(diǎn)說(shuō),這個(gè)網(wǎng)絡(luò)的訓(xùn)練可能就只需要微調(diào)一下,就能在B任務(wù)上顯示出非常好的效果

    下圖展示的是一個(gè)真實(shí)的Transfer Learning的過(guò)程,左邊是已經(jīng)training好的網(wǎng)絡(luò),我們利用這個(gè)網(wǎng)絡(luò)的公有部分,吸取它的common knowledge, 然后把最后一層去掉,換成我們需要的

    先上核心代碼

    import torch.nn as nn from torchvision.models import resnet18class Flatten(nn.Module):def __init__(self):super(Flatten, self).__init__()def forward(self, x):shape = torch.prod(torch.tensor(x.shape[1:])).item()return x.view(-1, shape) trained_model = resnet18(pretrained=True) model = nn.Sequential(*list(trained_model.children())[:-1],# [b, 512, 1, 1]Flatten(), # [b, 512, 1, 1] => [b, 512]nn.Linear(512, 5) # [b, 512] => [b, 5])

    PyTorch中有已經(jīng)訓(xùn)練好的各種規(guī)格的resnet,第一次使用需要下載。我們不要resnet18的最后一層,所以要用list(trained_model.children())[:-1]把除了最后一層以外的所有層都取出來(lái),保存在list中,然后用*將其list展開(kāi),之后接一個(gè)我們自定義的Flatten層,作用是將output打平,打平以后才能送到Linear層去

    上面幾行代碼就實(shí)現(xiàn)了Transfer Learning,而且不需要我們自己實(shí)現(xiàn)resnet,完整代碼如下

    import torch import os, glob import warnings import random, csv from PIL import Image from torch import optim, nn import torch.nn.functional as F from torchvision import transforms from torchvision.models import resnet18 from torch.utils.data import Dataset, DataLoader warnings.filterwarnings('ignore') from matplotlib import pyplot as pltclass Pokemon(Dataset):def __init__(self, root, resize, model):super(Pokemon, self).__init__()self.root = rootself.resize = resizeself.name2label = {} # 將文件夾的名字映射為label(數(shù)字)for name in sorted(os.listdir(os.path.join(root))):if not os.path.isdir(os.path.join(root, name)):continueself.name2label[name] = len(self.name2label.keys())# image, labelself.images, self.labels = self.load_csv('images.csv')if model == 'train': # 60%self.images = self.images[:int(0.6*len(self.images))]self.labels = self.labels[:int(0.6*len(self.labels))]elif model == 'val': # 20%self.images = self.images[int(0.6*len(self.images)):int(0.8*len(self.images))]self.labels = self.labels[int(0.6*len(self.labels)):int(0.8*len(self.labels))]else: # 20%self.images = self.images[int(0.8*len(self.images)):]self.labels = self.labels[int(0.8*len(self.labels)):]def load_csv(self, filename):if not os.path.exists(os.path.join(self.root, filename)):images = []for name in self.name2label.keys():images += glob.glob(os.path.join(self.root, name, '*.png'))images += glob.glob(os.path.join(self.root, name, '*.jpg'))images += glob.glob(os.path.join(self.root, name, '*.jpeg'))random.shuffle(images)with open(os.path.join(self.root, filename), mode='w', newline='') as f:writer = csv.writer(f)for img in images: # pokemon\\bulbasaur\\00000000.pngname = img.split(os.sep)[-2] # bulbasaurlabel = self.name2label[name]# pokemon\\bulbasaur\\00000000.png 0writer.writerow([img, label])print('writen into csv file:', filename)# read csv fileimages, labels = [], []with open(os.path.join(self.root, filename)) as f:reader = csv.reader(f)for row in reader:image, label = rowlabel = int(label)images.append(image)labels.append(label)assert len(images) == len(labels)return images, labelsdef __len__(self):return len(self.images)def __getitem__(self, idx):# idx [0~len(images)]# self.images, self.labels# pokemon\\bulbasaur\\00000000.png 0img, label = self.images[idx], self.labels[idx]tf = transforms.Compose([lambda x:Image.open(x).convert('RGB'), # string path => image datatransforms.Resize((int(self.resize*1.25), int(self.resize*1.25))),transforms.RandomRotation(15),transforms.CenterCrop(self.resize),transforms.ToTensor(),transforms.Normalize(mean=[0.485, 0.456, 0.406],std=[0.229, 0.224, 0.225])])img = tf(img)label = torch.tensor(label)return img, labelclass Flatten(nn.Module):def __init__(self):super(Flatten, self).__init__()def forward(self, x):shape = torch.prod(torch.tensor(x.shape[1:])).item()return x.view(-1, shape) batchsz = 32 lr = 1e-3 epochs = 10 device = torch.device('cuda') torch.manual_seed(1234)train_db = Pokemon('pokemon', 224, model='train') val_db = Pokemon('pokemon', 224, model='val') test_db = Pokemon('pokemon', 224, model='test') train_loader = DataLoader(train_db, batch_size=batchsz, shuffle=True, num_workers=2) val_loader = DataLoader(val_db, batch_size=batchsz, num_workers=2) test_loader = DataLoader(test_db, batch_size=batchsz, num_workers=2)def evalute(model, loader):correct = 0total = len(loader.dataset)for x,y in loader:with torch.no_grad():logits = model(x)pred = logits.argmax(dim=1)correct += torch.eq(pred, y).sum().float().item()return correct / totaldef main():trained_model = resnet18(pretrained=True)model = nn.Sequential(*list(trained_model.children())[:-1],# [b, 512, 1, 1]Flatten(), # [b, 512, 1, 1] => [b, 512]nn.Linear(512, 5))optimizer = optim.Adam(model.parameters(), lr=lr)criteon = nn.CrossEntropyLoss()best_acc, best_epoch = 0, 0for epoch in range(epochs):for step, (x, y) in enumerate(train_loader):# x:[b, 3, 224, 224], y:[b]logits = model(x)loss = criteon(logits, y)optimizer.zero_grad()loss.backward()optimizer.step()if epoch % 2 == 0:val_acc = evalute(model, val_loader)if val_acc > best_acc:best_epoch = epochbest_acc = val_acctorch.save(model.state_dict(), 'best.mdl')print('best acc:', best_acc, 'best_epoch', best_epoch)model.load_state_dict(torch.load('best.mdl'))print('loaded from ckt!')test_acc = evalute(model, test_loader)print('test_acc:', test_acc)if __name__ == '__main__':main()

    最終test accuracy在0.94左右,比我們自己從0開(kāi)始訓(xùn)練效果好了很多

    往期精彩回顧適合初學(xué)者入門人工智能的路線及資料下載機(jī)器學(xué)習(xí)在線手冊(cè)深度學(xué)習(xí)在線手冊(cè)AI基礎(chǔ)下載(pdf更新到25集)備注:加入本站微信群或者qq群,請(qǐng)回復(fù)“加群”獲取一折本站知識(shí)星球優(yōu)惠券,請(qǐng)回復(fù)“知識(shí)星球”喜歡文章,點(diǎn)個(gè)在看

    總結(jié)

    以上是生活随笔為你收集整理的AI入门:Transfer Learning(迁移学习)的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。

    如果覺(jué)得生活随笔網(wǎng)站內(nèi)容還不錯(cuò),歡迎將生活随笔推薦給好友。