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

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

【Pytorch神经网络实战案例】18 最大化深度互信信息模型DIM实现搜索最相关与最不相关的图片

發布時間:2024/7/5 编程问答 43 豆豆
生活随笔 收集整理的這篇文章主要介紹了 【Pytorch神经网络实战案例】18 最大化深度互信信息模型DIM实现搜索最相关与最不相关的图片 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

圖片搜索器分為圖片的特征提取和匹配兩部分,其中圖片的特征提取是關鍵。將使用一種基于無監督模型的提取特征的方法實現特征提取,即最大化深度互信息(DeepInfoMax,DIM)方法。

1 最大深度互信信息模型DIM簡介

在DIM模型中,結合了自編碼和對抗神經網絡,損失函數使用了MINE與f-GAN方法的結合。在此之上,DM模型又從全局損失、局部損失和先驗損失3個損失出發進行訓練。

1.1 DIM模型原理

性能好的編碼器應該能夠提取出樣本中最獨特、具體的信息,而不是單純地追求過小的重構誤差。而樣本的獨特信息可以使用互信息(MutualInformation,MI)來衡量。

因此,在DIM模型中,編碼器的目標函數不是最小化輸入與輸出的MSE,而是最大化輸入與輸出的互信息。

1.2 DIM模型的主要思想

DIM模型中的互信息解決方案主要來自MINE方法,即計算輸入樣本與編碼器輸出的轉征向量之間的互信息,通過最大化互信息來實現模型的訓練。

1.2.1 DIM模型在無監督訓練中的兩種約束。

  • 最大化輸入信息和高級特征向量之間的互信息:如果模型輸出的低維特征能夠代表輸入樣本,那么該特征分布與輸入樣本分布的互信息一定是最大的。
  • 對抗匹配先驗分布:編碼器輸出的高級特征要更接近高斯分布,判別器要將編碼器生成的數據分布與高斯分布區分。
  • 在實現時,DlM模型使用了3個判別器,分別從局部互信息最大化、全局互信息最大化和先驗分布匹配最小化的3個角度對編碼器的輸出結果進行約束。(論文arXv:1808.06670,2018)

    1.3 局部與全局互信息最大化約束的原理

    許多表示學習只使用已探索過的數據空間(稱為像素級別),當一小部分數據十分關心語義級別時,表明該表示學習將不利于訓練。
    ? ? 對于圖片,它的相關性更多體現在局部。圖片的識別、分類等應該是一個從局部到整體的過程,即全局特征更適合用于重構,局部特征更適合用于下游的分類任務。
    局部特征可以理解為卷積后得到的特征圖,全局特征可以理解為對特征圖進行編碼得到的特征向量。

    DIM模型從局部和全局兩個角度出發對輸入和輸出執行互信息計算。

    1.4 先驗分布匹配最小化約束原理

    先驗匹配的目的是對編碼器生成向量形式進行約束,使其更接近高斯分布。

    DIM模型的編碼器的主要思想是:在對輸入數據編碼成特征向量的同時,還希望這個特征向量服從于標準的高斯分布。這種做法使編碼空間更加規整,甚至有利于解耦特征,便于后續學習,與變分自編碼中編碼器的使命是一樣的。

    因此,在DIM模型中引入變分自編碼神經網絡的原理,將高斯分布當作先驗分布,對編碼器輸出的向量進行約束。

    2 DIM模型的結構

    2.1 DIM模型結構圖

    DIM模型的結構DIM模型由4個子模型構成:1個編碼器、3個判別器。其中解碼器的作用主要是對圖進行特征提取,3個判器需分別從局部、全局、先驗匹配3個角度對編碼器的輸出結果進行約束。

    2.2 DlM模型的特殊之處

    ? ? 在DlM模型的實際實現過程中,沒有直接對原始的輸入數據與編碼器輸出的特征數據執行最大化互信息計算,而使用了編碼器中間過程中的特征圖與最終的特征數據執行互信息計算。

    ? ? 根據MINE方法,利用神經網絡計算互信息的方法可以換算成計算兩個數據集的聯合分布和邊緣分布間的散度,即將判別器處理特征圖和特征數據的結果當作聯合分布,將亂序后的特征圖和特征數據輸入判別器得到邊緣分布。

    DIM模型打亂特征圖的批次順序后與編碼器輸出的提示特征向量一起作為判別器的輸入,即令輸入判別器的特征圖與特征向量各自獨立(破壞特征圖與特征向量間的對應關系),詳見互信息神經估計的原理介紹。

    2.3 全局判別器模型

    如圖8-29,全局判別器的輸入值有兩個:特征圖和特征數據y。在計算互信息的過程中,聯合分布的特征圖和特征數據y都來自編碼神經網絡的輸出。計算邊緣分布的特征圖是由改變特征圖的批次順序得來的,特征數據y來自編碼神經網絡的輸出,如圖8-30所示。

    在全局判別器中,具體的處理步驟如下。
    (1)使用卷積層對特征圖進行處理,得到全局特征。
    (2)將該全局特征與特征數據y用torch.cat()函數連接起來。
    (3)將連接后的結果輸入全連接網絡(對兩個全局特征進行判定),最終輸出判別結果(一維向量)。

    2.4 局部判別器模型

    如圖8-29所示,局部判別器的輸入值是一個特殊的合成向量:將編碼器輸出的特征數據y按照特征圖的尺寸復制成m×m份。令特征圖中的每個像素都與編碼器輸出的全局特征數據ν相連。這樣,判別器所做的事情就變成對每個像素與全局特征向量之間的互信息進行計算。因此,該判別器稱為局部判別器。
    在局部判別器中,計算互信息的聯合分布和邊緣分布方式與全局判別器一致,如圖8-31所示,在局部判別器中主要使用了1×1的卷積操作(步長也為1)。因為這種卷積操作不會改變特征圖的尺寸(只是通道數的變換),所以判別器的最終輸出也是大小為m×m的值。

    局部判別器通過執行多層的1×1卷積操作,將通道數最終變成1,并作為最終的判別結果。該過程可以理解為,同時對每個像素與全局特征計算互信息。

    2.5 先驗判別器模型

    先驗判別器模型主要是輔助編碼器生成的向量趨近于高斯分布,其做法與普通的對抗神經網絡一致。先驗判別器模型輸出的結果只有0或1:令判別器對高斯分布采樣的數據判定為真(1),對編碼器輸出的特征向量判定為假(0),如圖8-32所示。

    先驗判別器模型如圖8-32所示,先驗判別器模型的輸入只有一個特征向量。其結構主要使用了全連接神經網絡,最終會輸出“真”或“假”的判定結果。

    2.6 損失函數

    ? ? 在DIM模型中,將MINE方法中的KL散度換成JS散度來作為互信息的度量。這樣做的原因是:JS散度是有上界的,而KL散度是沒有上界的。相比之下,JS散度更適合在最大化任務中使用,因為它在計算時不會產生特別大的數,并且JS散度的梯度又是無偏的。

    在f-GAN中可以找到JS散度的計算公式,見式(8-46)(其原理在式(8-46)下面的提示部分進行了闡述)。

    ?先驗判別器的損失函數非常簡單、與原始的GAN模型(參見的論文編號為anXiv:1406.2661,2014)中的損失函數一致,對這3個判別器各自損失函數的計算結果加權求和,便得到整個DM模型的損失函數。

    3 實戰案例簡介與代碼實現(訓練模型代碼實現)

    使用最大化深度互信息模型提取圖片信息,并用提取出來的低維特征制作圖片搜索器。

    3.1 CIFAR數據集

    ? ? 本例使用的數據集是ClFAR,它與Fashion-MNIST數據集類似,也是一些圖片。ClFAR比Fashion-MNIST更為復雜,而且由彩色圖像組成,相比之下,與實際場景中接觸的樣本更為接近。

    3.1.1?CIFAR數據集的組成

    CIFAR數據集的版本因為起初的數據集共將數據分為10類,分別為飛機、汽車、鳥、貓、鹿、狗、青蛙、馬、船、卡車,所以ClFAR的數據集常以CIFAR-10命名,其中包含60000張32像素×32像素的彩色圖像(包含50000張訓練圖片、10000張測試圖片),沒有任何類型重疊的情況。因為是彩色圖像,所以這個數據集是三通道的,具有R、G、B這3個通道。

    CIFAR又推出了一個分類更多的版本:ClFAR-100,從名字也可以看出,其將數據分為100類。它將圖片分得更細,當然,這對神經網絡圖像識別是更大的挑戰,有了這數據,我們可以把精力全部投入在網絡優化上。

    ?3.2 獲取數據集

    ClFAR數據集是已經打包好的文件,分為Python、二進制bin文件包,方便不同的程序讀取,本次使用的數據集是ClFAR-10版本中的Python文件包,對應的文件名稱為“cifar-10-pyhon.tar.gz”。該文件可以在官網上手動下載,也可以使用與獲取Fashion-MNIST類似的方法,通過PyTorch的內嵌代碼進行下載。

    3.3 加載并顯示CIFAR數據集------DIM_CIRFAR_train.py(第1部分)

    import torch from torch import nn import torch.nn.functional as F import torchvision from torch.optim import Adam from torchvision.transforms import ToTensor from torch.utils.data import DataLoader from torchvision.datasets.cifar import CIFAR10 from matplotlib import pyplot as plt import numpy as np from tqdm import tqdm from pathlib import Path from torchvision.transforms import ToPILImage import os os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"# 1.1 獲取數據集并顯示數據集 # 指定運算設備 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(device) # 加載數據集 batch_size = 512 data_dir = r'./cifar10/' # 將CIFAR10數據集下載到本地:共有三份文件,標簽說明文件batches.meta,訓練樣本集data_batch_x(一共五個,包含10000條訓練樣本),測試樣本test.batch train_dataset = CIFAR10(data_dir,download=True,transform=ToTensor()) train_loader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True,drop_last=True,pin_memory=torch.cuda.is_available()) print("訓練樣本個數:",len(train_dataset)) # 定義函數用于顯示圖片 def imshowrow(imgs,nrow):plt.figure(dpi=200) # figsize=(9,4)# ToPILImage()調用PyTorch的內部轉換接口,實現張量===>PLImage類型圖片的轉換。# 該接口主要實現。(1)將張量的每個元素乘以255。(2)將張量的數據類型由FloatTensor轉化成uint8。(3)將張量轉化成NumPy的ndarray類型。(4)對ndarray對象執行transpose(1,2,0)的操作。(5)利用Image下的fromarray()函數,將ndarray對象轉化成PILImage形式。(6)輸出PILImage。_img = ToPILImage()(torchvision.utils.make_grid(imgs,nrow=nrow)) # 傳入PLlmage()接口的是由torchvision.utis.make_grid接口返回的張量對象plt.axis('off')plt.imshow(_img)plt.show()# 定義標簽與對應的字符 classes = ('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') # 獲取一部分樣本用于顯示 sample = iter(train_loader) images,labels = sample.next() print("樣本形狀:",np.shape(images)) print('樣本標簽:',','.join('%2d:%-5s' % (labels[j],classes[labels[j]]) for j in range(len(images[:10])))) imshowrow(images[:10],nrow=10)

    輸出:

    3.5?定義DIM模型------DIM_CIRFAR_train.py(第2部分)

    # 1.2 定義DIM模型 class Encoder(nn.Module): # 通過多個卷積層對輸入數據進行編碼,生成64維特征向量def __init__(self):super().__init__()self.c0 = nn.Conv2d(3, 64, kernel_size=4, stride=1) # 輸出尺寸29self.c1 = nn.Conv2d(64, 128, kernel_size=4, stride=1) # 輸出尺寸26self.c2 = nn.Conv2d(128, 256, kernel_size=4, stride=1) # 輸出尺寸23self.c3 = nn.Conv2d(256, 512, kernel_size=4, stride=1) # 輸出尺寸20self.l1 = nn.Linear(512*20*20, 64)# 定義BN層self.b1 = nn.BatchNorm2d(128)self.b2 = nn.BatchNorm2d(256)self.b3 = nn.BatchNorm2d(512)def forward(self, x):h = F.relu(self.c0(x))features = F.relu(self.b1(self.c1(h)))#輸出形狀[b 128 26 26]h = F.relu(self.b2(self.c2(features)))h = F.relu(self.b3(self.c3(h)))encoded = self.l1(h.view(x.shape[0], -1))# 輸出形狀[b 64]return encoded, featuresclass DeepInfoMaxLoss(nn.Module): # 實現全局、局部、先驗判別器模型的結構設計,合并每個判別器的損失函數,得到總的損失函數def __init__(self,alpha=0.5,beta=1.0,gamma=0.1):super().__init__()# 初始化損失函數的加權參數self.alpha = alphaself.beta = betaself.gamma = gamma# 定義局部判別模型self.local_d = nn.Sequential(nn.Conv2d(192,512,kernel_size=1),nn.ReLU(True),nn.Conv2d(512,512,kernel_size=1),nn.ReLU(True),nn.Conv2d(512,1,kernel_size=1))# 定義先驗判別器模型self.prior_d = nn.Sequential(nn.Linear(64,1000),nn.ReLU(True),nn.Linear(1000,200),nn.ReLU(True),nn.Linear(200,1),nn.Sigmoid() # 在定義先驗判別器模型的結構時,最后一層的激活函數用Sigmoid函數。這是原始GAN模型的標準用法(可以控制輸出值的范圍為0-1),是與損失函數配套使用的。)# 定義全局判別器模型self.global_d_M = nn.Sequential(nn.Conv2d(128,64,kernel_size=3), # 輸出形狀[b,64,24,24]nn.ReLU(True),nn.Conv2d(64,32,kernel_size=3), # 輸出形狀 [b,32,32,22]nn.Flatten(),)self.global_d_fc = nn.Sequential(nn.Linear(32*22*22+64,512),nn.ReLU(True),nn.Linear(512,512),nn.ReLU(True),nn.Linear(512,1))def GlobalD(self, y, M):h = self.global_d_M(M)h = torch.cat((y, h), dim=1)return self.global_d_fc(h)def forward(self,y,M,M_prime):# 復制特征向量y_exp = y.unsqueeze(-1).unsqueeze(-1)y_exp = y_exp.expand(-1,-1,26,26) # 輸出形狀[b,64,26,26]# 按照特征圖的像素連接特征向量y_M = torch.cat((M,y_exp),dim=1) # 輸出形狀[b,192,26,26]y_M_prime = torch.cat((M_prime,y_exp),dim=1)# 輸出形狀[b,192,26,26]# 計算局部互信息---互信息的計算Ej = -F.softplus(-self.local_d(y_M)).mean() # 聯合分布Em = F.softplus(self.local_d(y_M_prime)).mean() # 邊緣分布LOCAL = (Em - Ej) * self.beta # 最大化互信息---對互信息執行了取反操作。將最大化問題變為最小化問題,在訓練過程中,可以使用最小化損失的方法進行處理。# 計算全局互信息---互信息的計算Ej = -F.softplus(-self.GlobalD(y, M)).mean() # 聯合分布Em = F.softplus(self.GlobalD(y, M_prime)).mean() # 邊緣分布GLOBAL = (Em - Ej) * self.alpha # 最大化互信息---對互信息執行了取反操作。將最大化問題變為最小化問題,在訓練過程中,可以使用最小化損失的方法進行處理。# 計算先驗損失prior = torch.rand_like(y) # 獲得隨機數term_a = torch.log(self.prior_d(prior)).mean() # GAN損失term_b = torch.log(1.0 - self.prior_d(y)).mean()PRIOR = -(term_a + term_b) * self.gamma # 最大化目標分布---實現了判別器的損失函數。判別器的目標是將真實數據和生成數據的分布最大化,因此,也需要取反,通過最小化損失的方法來實現。return LOCAL + GLOBAL + PRIOR# #### 在訓練過程中,梯度可以通過損失函數直接傳播到編碼器模型,進行聯合優化,因此,不需要對編碼器額外進行損失函數的定義!

    3.6?實例化DIM模型并訓練------DIM_CIRFAR_train.py(第3部分)

    # 1.3 實例化DIM模型并訓練:實例化模型按照指定次數迭代訓練。在制作邊緣分布樣本時,將批次特征圖的第1條放到最后,以使特征圖與特征向量無法對應,實現與按批次打亂順序等同的效果。 totalepoch = 100 # 指定訓練次數 if __name__ == '__main__':encoder =Encoder().to(device)loss_fn = DeepInfoMaxLoss().to(device)optim = Adam(encoder.parameters(),lr=1e-4)loss_optim = Adam(loss_fn.parameters(),lr=1e-4)epoch_loss = []for epoch in range(totalepoch +1):batch = tqdm(train_loader,total=len(train_dataset)//batch_size)train_loss = []for x,target in batch: # 遍歷數據集x = x.to(device)optim.zero_grad()loss_optim.zero_grad()y,M = encoder(x) # 用編碼器生成特征圖和特征向量# 制作邊緣分布樣本M_prime = torch.cat((M[1:],M[0].unsqueeze(0)),dim=0)loss =loss_fn(y,M,M_prime) # 計算損失train_loss.append(loss.item())batch.set_description(str(epoch) + ' Loss:%.4f'% np.mean(train_loss[-20:]))loss.backward()optim.step() # 調用編碼器優化器loss_optim.step() # 調用判別器優化器if epoch % 10 == 0 : # 保存模型root = Path(r'./DIMmodel/')enc_file = root / Path('encoder' + str(epoch) + '.pth')loss_file = root / Path('loss' + str(epoch) + '.pth')enc_file.parent.mkdir(parents=True, exist_ok=True)torch.save(encoder.state_dict(), str(enc_file))torch.save(loss_fn.state_dict(), str(loss_file))epoch_loss.append(np.mean(train_loss[-20:])) # 收集訓練損失plt.plot(np.arange(len(epoch_loss)), epoch_loss, 'r') # 損失可視化plt.show()

    結果:

    ?

    3.7 加載模型并搜索圖片------DIM_CIRFAR_loadpath.py

    import torch import torch.nn.functional as F from tqdm import tqdm import random# 功能介紹:載入編碼器模型,對樣本集中所有圖片進行編碼,隨機取一張圖片,找出與該圖片最接近與最不接近的十張圖片 # # 引入本地庫 #引入本地代碼庫 from DIM_CIRFAR_train import ( train_loader,train_dataset,totalepoch,device,batch_size,imshowrow, Encoder)# 加載模型 model_path = r'./DIMmodel/encoder%d.pth'% (totalepoch) encoder = Encoder().to(device) encoder.load_state_dict(torch.load(model_path,map_location=device))# 加載模型樣本,并調用編碼器生成特征向量 batchesimg = [] batchesenc = [] batch = tqdm(train_loader,total=len(train_dataset)//batch_size) for images ,target in batch :images = images.to(device)with torch.no_grad():encoded,features = encoder(images) # 調用編碼器生成特征向量batchesimg.append(images)batchesenc.append(encoded) # 將樣本中的圖片與生成的向量沿第1維度展開 batchesenc = torch.cat(batchesenc,axis = 0) batchesimg = torch.cat(batchesimg,axis = 0) # 驗證向量的搜索功能 index = random.randrange(0,len(batchesenc)) # 隨機獲取一個索引,作為目標圖片 batchesenc[index].repeat(len(batchesenc),1) # 將目標圖片的特征向量復制多份 # 使用F.mse_loss()函數進行特征向量間的L2計算,傳入了參數reduction='none',這表明對計算后的結果不執行任何操作。如果不傳入該參數,那么函數默認會對所有結果取平均值(常用在訓練模型場景中) l2_dis = F.mse_loss(batchesenc[index].repeat(len(batchesenc),1),batchesenc,reduction='none').sum(1) # 計算目標圖片與每個圖片的L2距離 findnum = 10 # 設置查找圖片的個數 # 使用topk()方法獲取L2距離最近、最遠的圖片。該方法會返回兩個值,第一個是真實的比較值,第二個是該值對應的索引。 _,indices = l2_dis.topk(findnum,largest=False ) # 查找10個最相近的圖片 _,indices_far = l2_dis.topk(findnum,) # 查找10個最不相關的圖片 # 顯示結果 indices = torch.cat([torch.tensor([index]).to(device),indices]) indices_far = torch.cat([torch.tensor([index]).to(device),indices_far]) rel = torch.cat([batchesimg[indices],batchesimg[indices_far]],axis = 0) imshowrow(rel.cpu() ,nrow=len(indices)) # 結果顯示:結果有兩行,每行的第一列是目標圖片,第一行是與目標圖片距離最近的搜索結果,第二行是與目標圖片距離最遠的搜索結果。

    ?4 代碼總覽

    4.1 訓練模型:DIM_CIRFAR_train.py

    import torch from torch import nn import torch.nn.functional as F import torchvision from torch.optim import Adam from torchvision.transforms import ToTensor from torch.utils.data import DataLoader from torchvision.datasets.cifar import CIFAR10 from matplotlib import pyplot as plt import numpy as np from tqdm import tqdm from pathlib import Path from torchvision.transforms import ToPILImage import os os.environ["KMP_DUPLICATE_LIB_OK"]="TRUE"# 1.1 獲取數據集并顯示數據集 # 指定運算設備 device = torch.device('cuda' if torch.cuda.is_available() else 'cpu') print(device) # 加載數據集 batch_size = 512 data_dir = r'./cifar10/' # 將CIFAR10數據集下載到本地:共有三份文件,標簽說明文件batches.meta,訓練樣本集data_batch_x(一共五個,包含10000條訓練樣本),測試樣本test.batch train_dataset = CIFAR10(data_dir,download=True,transform=ToTensor()) train_loader = DataLoader(train_dataset,batch_size=batch_size,shuffle=True,drop_last=True,pin_memory=torch.cuda.is_available()) print("訓練樣本個數:",len(train_dataset)) # 定義函數用于顯示圖片 def imshowrow(imgs,nrow):plt.figure(dpi=200) # figsize=(9,4)# ToPILImage()調用PyTorch的內部轉換接口,實現張量===>PLImage類型圖片的轉換。# 該接口主要實現。(1)將張量的每個元素乘以255。(2)將張量的數據類型由FloatTensor轉化成uint8。(3)將張量轉化成NumPy的ndarray類型。(4)對ndarray對象執行transpose(1,2,0)的操作。(5)利用Image下的fromarray()函數,將ndarray對象轉化成PILImage形式。(6)輸出PILImage。_img = ToPILImage()(torchvision.utils.make_grid(imgs,nrow=nrow)) # 傳入PLlmage()接口的是由torchvision.utis.make_grid接口返回的張量對象plt.axis('off')plt.imshow(_img)plt.show()# 定義標簽與對應的字符 classes = ('airplane', 'automobile', 'bird', 'cat', 'deer', 'dog', 'frog', 'horse', 'ship', 'truck') # 獲取一部分樣本用于顯示 sample = iter(train_loader) images,labels = sample.next() print("樣本形狀:",np.shape(images)) print('樣本標簽:',','.join('%2d:%-5s' % (labels[j],classes[labels[j]]) for j in range(len(images[:10])))) imshowrow(images[:10],nrow=10)# 1.2 定義DIM模型 class Encoder(nn.Module): # 通過多個卷積層對輸入數據進行編碼,生成64維特征向量def __init__(self):super().__init__()self.c0 = nn.Conv2d(3, 64, kernel_size=4, stride=1) # 輸出尺寸29self.c1 = nn.Conv2d(64, 128, kernel_size=4, stride=1) # 輸出尺寸26self.c2 = nn.Conv2d(128, 256, kernel_size=4, stride=1) # 輸出尺寸23self.c3 = nn.Conv2d(256, 512, kernel_size=4, stride=1) # 輸出尺寸20self.l1 = nn.Linear(512*20*20, 64)# 定義BN層self.b1 = nn.BatchNorm2d(128)self.b2 = nn.BatchNorm2d(256)self.b3 = nn.BatchNorm2d(512)def forward(self, x):h = F.relu(self.c0(x))features = F.relu(self.b1(self.c1(h)))#輸出形狀[b 128 26 26]h = F.relu(self.b2(self.c2(features)))h = F.relu(self.b3(self.c3(h)))encoded = self.l1(h.view(x.shape[0], -1))# 輸出形狀[b 64]return encoded, featuresclass DeepInfoMaxLoss(nn.Module): # 實現全局、局部、先驗判別器模型的結構設計,合并每個判別器的損失函數,得到總的損失函數def __init__(self,alpha=0.5,beta=1.0,gamma=0.1):super().__init__()# 初始化損失函數的加權參數self.alpha = alphaself.beta = betaself.gamma = gamma# 定義局部判別模型self.local_d = nn.Sequential(nn.Conv2d(192,512,kernel_size=1),nn.ReLU(True),nn.Conv2d(512,512,kernel_size=1),nn.ReLU(True),nn.Conv2d(512,1,kernel_size=1))# 定義先驗判別器模型self.prior_d = nn.Sequential(nn.Linear(64,1000),nn.ReLU(True),nn.Linear(1000,200),nn.ReLU(True),nn.Linear(200,1),nn.Sigmoid() # 在定義先驗判別器模型的結構時,最后一層的激活函數用Sigmoid函數。這是原始GAN模型的標準用法(可以控制輸出值的范圍為0-1),是與損失函數配套使用的。)# 定義全局判別器模型self.global_d_M = nn.Sequential(nn.Conv2d(128,64,kernel_size=3), # 輸出形狀[b,64,24,24]nn.ReLU(True),nn.Conv2d(64,32,kernel_size=3), # 輸出形狀 [b,32,32,22]nn.Flatten(),)self.global_d_fc = nn.Sequential(nn.Linear(32*22*22+64,512),nn.ReLU(True),nn.Linear(512,512),nn.ReLU(True),nn.Linear(512,1))def GlobalD(self, y, M):h = self.global_d_M(M)h = torch.cat((y, h), dim=1)return self.global_d_fc(h)def forward(self,y,M,M_prime):# 復制特征向量y_exp = y.unsqueeze(-1).unsqueeze(-1)y_exp = y_exp.expand(-1,-1,26,26) # 輸出形狀[b,64,26,26]# 按照特征圖的像素連接特征向量y_M = torch.cat((M,y_exp),dim=1) # 輸出形狀[b,192,26,26]y_M_prime = torch.cat((M_prime,y_exp),dim=1)# 輸出形狀[b,192,26,26]# 計算局部互信息---互信息的計算Ej = -F.softplus(-self.local_d(y_M)).mean() # 聯合分布Em = F.softplus(self.local_d(y_M_prime)).mean() # 邊緣分布LOCAL = (Em - Ej) * self.beta # 最大化互信息---對互信息執行了取反操作。將最大化問題變為最小化問題,在訓練過程中,可以使用最小化損失的方法進行處理。# 計算全局互信息---互信息的計算Ej = -F.softplus(-self.GlobalD(y, M)).mean() # 聯合分布Em = F.softplus(self.GlobalD(y, M_prime)).mean() # 邊緣分布GLOBAL = (Em - Ej) * self.alpha # 最大化互信息---對互信息執行了取反操作。將最大化問題變為最小化問題,在訓練過程中,可以使用最小化損失的方法進行處理。# 計算先驗損失prior = torch.rand_like(y) # 獲得隨機數term_a = torch.log(self.prior_d(prior)).mean() # GAN損失term_b = torch.log(1.0 - self.prior_d(y)).mean()PRIOR = -(term_a + term_b) * self.gamma # 最大化目標分布---實現了判別器的損失函數。判別器的目標是將真實數據和生成數據的分布最大化,因此,也需要取反,通過最小化損失的方法來實現。return LOCAL + GLOBAL + PRIOR# #### 在訓練過程中,梯度可以通過損失函數直接傳播到編碼器模型,進行聯合優化,因此,不需要對編碼器額外進行損失函數的定義!# 1.3 實例化DIM模型并訓練:實例化模型按照指定次數迭代訓練。在制作邊緣分布樣本時,將批次特征圖的第1條放到最后,以使特征圖與特征向量無法對應,實現與按批次打亂順序等同的效果。 totalepoch = 10 # 指定訓練次數 if __name__ == '__main__':encoder =Encoder().to(device)loss_fn = DeepInfoMaxLoss().to(device)optim = Adam(encoder.parameters(),lr=1e-4)loss_optim = Adam(loss_fn.parameters(),lr=1e-4)epoch_loss = []for epoch in range(totalepoch +1):batch = tqdm(train_loader,total=len(train_dataset)//batch_size)train_loss = []for x,target in batch: # 遍歷數據集x = x.to(device)optim.zero_grad()loss_optim.zero_grad()y,M = encoder(x) # 用編碼器生成特征圖和特征向量# 制作邊緣分布樣本M_prime = torch.cat((M[1:],M[0].unsqueeze(0)),dim=0)loss =loss_fn(y,M,M_prime) # 計算損失train_loss.append(loss.item())batch.set_description(str(epoch) + ' Loss:%.4f'% np.mean(train_loss[-20:]))loss.backward()optim.step() # 調用編碼器優化器loss_optim.step() # 調用判別器優化器if epoch % 10 == 0 : # 保存模型root = Path(r'./DIMmodel/')enc_file = root / Path('encoder' + str(epoch) + '.pth')loss_file = root / Path('loss' + str(epoch) + '.pth')enc_file.parent.mkdir(parents=True, exist_ok=True)torch.save(encoder.state_dict(), str(enc_file))torch.save(loss_fn.state_dict(), str(loss_file))epoch_loss.append(np.mean(train_loss[-20:])) # 收集訓練損失plt.plot(np.arange(len(epoch_loss)), epoch_loss, 'r') # 損失可視化plt.show()

    4.2 加載模型:DIM_CIRFAR_loadpath.py

    import torch import torch.nn.functional as F from tqdm import tqdm import random# 功能介紹:載入編碼器模型,對樣本集中所有圖片進行編碼,隨機取一張圖片,找出與該圖片最接近與最不接近的十張圖片 # # 引入本地庫 #引入本地代碼庫 from DIM_CIRFAR_train import ( train_loader,train_dataset,totalepoch,device,batch_size,imshowrow, Encoder)# 加載模型 model_path = r'./DIMmodel/encoder%d.pth'% (totalepoch) encoder = Encoder().to(device) encoder.load_state_dict(torch.load(model_path,map_location=device))# 加載模型樣本,并調用編碼器生成特征向量 batchesimg = [] batchesenc = [] batch = tqdm(train_loader,total=len(train_dataset)//batch_size) for images ,target in batch :images = images.to(device)with torch.no_grad():encoded,features = encoder(images) # 調用編碼器生成特征向量batchesimg.append(images)batchesenc.append(encoded) # 將樣本中的圖片與生成的向量沿第1維度展開 batchesenc = torch.cat(batchesenc,axis = 0) batchesimg = torch.cat(batchesimg,axis = 0) # 驗證向量的搜索功能 index = random.randrange(0,len(batchesenc)) # 隨機獲取一個索引,作為目標圖片 batchesenc[index].repeat(len(batchesenc),1) # 將目標圖片的特征向量復制多份 # 使用F.mse_loss()函數進行特征向量間的L2計算,傳入了參數reduction='none',這表明對計算后的結果不執行任何操作。如果不傳入該參數,那么函數默認會對所有結果取平均值(常用在訓練模型場景中) l2_dis = F.mse_loss(batchesenc[index].repeat(len(batchesenc),1),batchesenc,reduction='none').sum(1) # 計算目標圖片與每個圖片的L2距離 findnum = 10 # 設置查找圖片的個數 # 使用topk()方法獲取L2距離最近、最遠的圖片。該方法會返回兩個值,第一個是真實的比較值,第二個是該值對應的索引。 _,indices = l2_dis.topk(findnum,largest=False ) # 查找10個最相近的圖片 _,indices_far = l2_dis.topk(findnum,) # 查找10個最不相關的圖片 # 顯示結果 indices = torch.cat([torch.tensor([index]).to(device),indices]) indices_far = torch.cat([torch.tensor([index]).to(device),indices_far]) rel = torch.cat([batchesimg[indices],batchesimg[indices_far]],axis = 0) imshowrow(rel.cpu() ,nrow=len(indices)) # 結果顯示:結果有兩行,每行的第一列是目標圖片,第一行是與目標圖片距離最近的搜索結果,第二行是與目標圖片距離最遠的搜索結果。

    總結

    以上是生活随笔為你收集整理的【Pytorch神经网络实战案例】18 最大化深度互信信息模型DIM实现搜索最相关与最不相关的图片的全部內容,希望文章能夠幫你解決所遇到的問題。

    如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

    主站蜘蛛池模板: av中文在线观看 | 91高清在线视频 | 天天噜夜夜噜 | 羞羞在线观看 | 香蕉福利视频 | 国产调教在线观看 | 91精品国产综合久久久久 | 国产精品36p | 国产欧美日韩亚洲 | 夜av| 色婷婷色婷婷 | 超碰在线99 | 亚洲播播 | 亚洲天堂一区在线观看 | 少妇太爽了在线观看 | 麻豆精品在线 | 国产日韩欧美精品 | av免费网页| av在线免费播放网址 | 午夜羞羞羞| 九九九精品视频 | 五月天中文字幕 | 日韩欧美一区二区三区四区 | 亚洲av无码国产综合专区 | 成年人黄色免费网站 | 成人亚洲精品 | 国产丝袜在线 | 特黄一区二区 | 国产精品8888| 国产91页| 中文字幕免费视频 | 成人免费做受小说 | 欧美日韩一区免费 | 国产黄色网址在线观看 | 麻豆免费观看视频 | 男人天堂av电影 | 绿帽在线| 蜜臀精品一区二区三区 | 少女与动物高清版在线观看 | 国产偷啪| 男女啪啪网站免费 | 91精品久久久久久久久久久 | 日本国产在线播放 | 人妖粗暴刺激videos呻吟 | 日本视频www色 | 日韩免费高清 | 欧美wwwxxxx| 日韩精品第一页 | 亚洲视频在线观看一区二区 | 亚洲精品成人无码毛片 | 奇米影视一区二区三区 | 成人久久久精品国产乱码一区二区 | 久久中文娱乐网 | se婷婷| 91黑人精品一区二区三区 | 香蕉视频在线播放 | 免费黄网在线看 | 精品国产视频一区二区 | 日日夜夜干 | 两个人看的www视频免费完整版 | 亚洲一区二区三区高清 | 欧美性猛交久久久乱大交小说 | 69黄色片 | 少妇超碰 | 午夜剧场91 | 欧美激情视频网 | 精品在线视频一区二区三区 | 亚洲情热 | 少妇无套高潮一二三区 | 91丝袜国产在线观看 | 亚洲国产网站 | 午夜成人免费电影 | 久久机热这里只有精品 | 狠久久| 国产精品无码一区二区无人区多人 | 欧美三级黄色大片 | 91天堂视频 | 一级黄色片片 | 先锋av资源在线 | 成人午夜激情视频 | 亚洲精品一区二区三区在线观看 | 日日夜夜狠狠操 | mm1313亚洲国产精品无码试看 | 蜜桃av一区二区三区 | 翔田千里x88aⅴ | 黄色视屏在线看 | 狠狠干天天爱 | a视频在线看 | 国产精品久久久久久久久免费相片 | 国产欧美日| 欧美偷拍少妇精品一区 | 丝袜美腿中文字幕 | 影视先锋av资源 | 亚洲 欧美 中文字幕 | 成人三级在线视频 | 福利二区 | 久久久久久99精品 | 精品久久久久久久久久久久久久久久 | 自拍三级|