model2=model1
會(huì)出現(xiàn)當(dāng)更新model2時(shí),model1的權(quán)重也會(huì)更新,這和自己的初始目的不同。
model2=copy.deepcopy(model1)
來(lái)實(shí)現(xiàn)深拷貝,手上沒(méi)有pytorch環(huán)境,具體還沒(méi)測(cè)試過(guò),誰(shuí)測(cè)試過(guò)可以和我說(shuō)下有沒(méi)有用。
所有要使用模型復(fù)制可以使用如下方法。
torch.save(model, "net_params.pkl") model5=Cnn(3,10) model5=torch.load('net_params.pkl')
這樣編寫(xiě)不會(huì)影響原始模型的權(quán)重
補(bǔ)充:pytorch模型訓(xùn)練流程中遇到的一些坑(持續(xù)更新)
要訓(xùn)練一個(gè)模型,主要分成幾個(gè)部分,如下。
入門(mén)的話(huà)肯定是拿 MNIST 手寫(xiě)數(shù)據(jù)集先練習(xí)。
pytorch 中有幫助我們制作數(shù)據(jù)生成器的模塊,其中有 Dataset、TensorDataset、DataLoader 等類(lèi)可以來(lái)創(chuàng)建數(shù)據(jù)入口。
之前在 tensorflow 中可以用 dataset.from_generator() 的形式,pytorch 中也類(lèi)似,目前我了解到的有兩種方法可以實(shí)現(xiàn)。
第一種就繼承 pytorch 定義的 dataset,改寫(xiě)其中的方法即可。如下,就獲得了一個(gè) DataLoader 生成器。
class MyDataset(Dataset): def __init__(self, data, labels): self.data = data self.labels = labels def __getitem__(self, index): return self.data[index], self.labels[index] def __len__(self): return len(self.labels) train_dataset = MyDataset(train_data, train_label) train_loader = DataLoader(dataset = train_dataset, batch_size = 1, shuffle = True)
第二種就是轉(zhuǎn)換,先把我們準(zhǔn)備好的數(shù)據(jù)轉(zhuǎn)化成 pytorch 的變量(或者是 Tensor),然后傳入 TensorDataset,再構(gòu)造 DataLoader。
X = torch.from_numpy(train_data).float() Y = torch.from_numpy(train_label).float() train_dataset = TensorDataset(X, Y) train_loader = DataLoader(dataset = train_dataset, batch_size = 1, shuffle = True) #num_workers = 2)
class Net(nn.Module): def __init__(self): super(Net, self).__init__() self.conv1 = nn.Conv2d(1, 6, 3) self.conv2 = nn.Conv2d(6 ,16, 3) self.fc1 = nn.Linear(400, 120) self.fc2 = nn.Linear(120, 84) self.fc3 = nn.Linear(84, 10) def forward(self, x): relu = F.relu(self.conv1(x)) x = F.max_pool2d(relu, (2, 2)) x = F.max_pool2d(F.relu(self.conv2(x)), 2) x = x.view(-1, self.num_flat_features(x)) x = F.relu(self.fc1(x)) x = F.relu(self.fc2(x)) x = self.fc3(x) return x def num_flat_features(self, x): size = x.size()[1:] #除了batch_size之外的維度 num_features = 1 for s in size: num_features *= s return num_features
訓(xùn)練模型那么肯定要先定義一個(gè)網(wǎng)絡(luò)結(jié)構(gòu),如上定義一個(gè)前向傳播網(wǎng)絡(luò)。里面包含了卷積層、全連接層、最大池化層和 relu 非線(xiàn)性激活層(名字我自己取的)以及一個(gè) view 展開(kāi),把一個(gè)多維的特征圖平展成一維的。
其中nn.Conv2d(in_channels, out_channels, kernel_size),第一個(gè)參數(shù)是輸入的深度,第二是輸出的深度,第三是卷積核的尺寸。
F.max_pool2d(input, (pool_size, pool_size)),第二個(gè)參數(shù)是池話(huà)
nn.Linear(in_features, out_features)
x.view是平展的操作,不過(guò)實(shí)際上相當(dāng)于 numpy 的 reshape,需要計(jì)算轉(zhuǎn)換后的尺寸。
import torch.optim as optim criterion = nn.CrossEntropyLoss() optimizer = optim.SGD(net.parameters(), lr=0.001, momentum=0.9)
模型定義完之后,意味著給出輸入,就可以得到輸出的結(jié)果。那么就來(lái)比較 outputs 和 targets 之間的區(qū)別,那么就需要用到損失函數(shù)來(lái)描述。
for epoch in range(2): # loop over the dataset multiple times running_loss = 0.0 for i, data in enumerate(trainloader, 0): # get the inputs; data is a list of [inputs, labels] inputs, labels = data # zero the parameter gradients optimizer.zero_grad() # forward + backward + optimize outputs = net(inputs) loss = criterion(outputs, labels) loss.backward() optimizer.step() # print statistics running_loss += loss.item() if i % 2000 == 1999: # print every 2000 mini-batches print('[%d, %5d] loss: %.3f' % (epoch + 1, i + 1, running_loss / 2000)) running_loss = 0.0 print('Finished Training')
以上的代碼是官方教程中給出來(lái)的,我們要做的就是學(xué)習(xí)他的思路。
1.首先是 epoch 的數(shù)量為 2,每個(gè) epoch 都會(huì)歷遍一次整個(gè)訓(xùn)練集。在每個(gè) epoch 內(nèi)累積統(tǒng)計(jì) running_loss,每 2000 個(gè) batch 數(shù)據(jù)計(jì)算一次損失的平均值,然后 print 再重新將 running_loss 置為 0。
2.然后分 mini-batch 進(jìn)行訓(xùn)練,在每個(gè)計(jì)算每個(gè) mini-batch 的損失之前,都會(huì)將優(yōu)化器 optimizer 中的梯度清空,防止不同 mini-batch 的梯度被累加到一起。更新分成兩步:第一步計(jì)算損失函數(shù),然后把總的損失分配到各個(gè)層中,即 loss.backward(),然后就使用優(yōu)化器更新權(quán)重,即 optimizer.step()。
PATH = '...' torch.save(net.state_dict(), PATH)
總的來(lái)說(shuō)流程就是上面那幾步,但自己做的時(shí)候就遇到了挺多問(wèn)題,最主要是對(duì)于其中張量傳播過(guò)程中的要求不清楚,導(dǎo)致出了不少錯(cuò)誤。
首先是輸入的數(shù)據(jù),pytorch 默認(rèn)圖片的 batch 數(shù)據(jù)的結(jié)構(gòu)是(BATCH_SIZE, CHANNELS, IMG_H, IMG_W),所以要在生成數(shù)據(jù)時(shí)做一些調(diào)整,滿(mǎn)足這種 BCHW 的規(guī)則。
會(huì)經(jīng)常出現(xiàn)一些某個(gè)矩陣或者張量要求的數(shù)據(jù),例如 “RuntimeError: Expected object of scalar type Double but got scalar type Float for argument #2 ‘mat2'” 等錯(cuò)誤信息。
可以使用 x.double(),y.float(),z.long() 等方式轉(zhuǎn)換成他要求的格式。
RuntimeError: multi-target not supported。這個(gè)錯(cuò)誤出現(xiàn)在損失函數(shù)那個(gè)地方,對(duì)于分類(lèi)問(wèn)題肯定是優(yōu)先考慮交叉熵。
criterion = nn.CrossEntropyLoss() loss = criterion(outputs, labels.long())#報(bào)錯(cuò)的地方
當(dāng)我batch-size=1時(shí)這個(gè)地方不會(huì)報(bào)錯(cuò),但是當(dāng)batch-size>1時(shí)就會(huì)報(bào)錯(cuò)。
查了別人的代碼,大家基本都是和官方教程里面寫(xiě)的一樣,使用官方的 mnist 數(shù)據(jù)接口,代碼如下。一開(kāi)始我是不愿意的,因?yàn)槟菢幼右馕吨赡軘?shù)據(jù)格式被封裝起來(lái)看不見(jiàn),但是自己折騰成本比較高,所以還是試了,真香!
train_dataset = datasets.MNIST(root='./data/', train=True, transform=transforms.ToTensor(), download=True) train_loader = DataLoader(dataset = train_dataset, batch_size = 4, shuffle = True)
打印了一下從生成器中獲得數(shù)據(jù),看一下 size,發(fā)現(xiàn)果然和我自己寫(xiě)的不同。當(dāng) batch_size=4 時(shí),數(shù)據(jù) data.size() 都是4*1*28*28,這個(gè)是相同的;但是 labels.size() 是不同的,我寫(xiě)的是 one_hot 向量所以是 4*10,但它的是 4。
直接打印 labels 看看,果然,是單個(gè)指,例如 tensor([3, 2, 6, 2]) 這樣。
不過(guò)模型的 outputs 依然是 4*10,看來(lái)是 nn.CrossEntropyLoss() 這個(gè)函數(shù)自己會(huì)做計(jì)算,所以他才會(huì)報(bào)錯(cuò)說(shuō) multi-target not supported,因?yàn)?lables.size() 不對(duì),原本只有一個(gè)數(shù)字,但現(xiàn)在是10個(gè)數(shù)字,相當(dāng)于被分配了10個(gè)屬性,自然就報(bào)錯(cuò)啦。
所以稍微修改了自己寫(xiě)的生成器之后,就沒(méi)問(wèn)題了。
不過(guò),如果想要更自由的調(diào)用數(shù)據(jù),還是需要對(duì)對(duì)象進(jìn)行一些方法的重載,使用 pytoch 定義的 DataLoader,用 enumerate,就會(huì)把所有的數(shù)據(jù)歷遍一次,如果使用 iter() 得到一個(gè)可迭代對(duì)象之后 next(),并不可以像 tensorflow 那樣子生成訓(xùn)練數(shù)據(jù)。
例如說(shuō),如果使用如上的形式,DataLoader 得到的是一個(gè)生成器,python 中的生成器對(duì)象主要有 __next__ 和 __iter__ 等魔術(shù)方法決定。
__iter__ 方法使得實(shí)例可以如下調(diào)用,可以得到一個(gè)可迭代對(duì)象,iterable,但是如果不加也沒(méi)關(guān)系,因?yàn)楦匾氖?__next__ 類(lèi)方法。
如下自己寫(xiě)了 __next__ 方法之后就可以看到,原本會(huì)出現(xiàn)越界的現(xiàn)象不見(jiàn)了,可以循環(huán)的歷遍數(shù)據(jù),當(dāng)然也可以想被注釋的那部分一樣,拋出 StopIteration 來(lái)終止。
a = A() a_iter = iter(a) class A(): def __init__(self): self.list = [1,2,3] self.index = 0 #def __getitem__(self, index): # return self.list[i] #def __iter__(self): # return self def __next__(self): #for i in range(): if self.index >= len(self.list): #raise StopIteration self.index = self.index%len(self.list) result = self.list[self.index] self.index += 1 return result b = A() for i in range(20): print(next(b))
以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教。
標(biāo)簽:濟(jì)源 阜新 昭通 淘寶好評(píng)回訪(fǎng) 隨州 信陽(yáng) 興安盟 合肥
巨人網(wǎng)絡(luò)通訊聲明:本文標(biāo)題《解決pytorch 模型復(fù)制的一些問(wèn)題》,本文關(guān)鍵詞 解決,pytorch,模型,復(fù)制,的,;如發(fā)現(xiàn)本文內(nèi)容存在版權(quán)問(wèn)題,煩請(qǐng)?zhí)峁┫嚓P(guān)信息告之我們,我們將及時(shí)溝通與處理。本站內(nèi)容系統(tǒng)采集于網(wǎng)絡(luò),涉及言論、版權(quán)與本站無(wú)關(guān)。