Pytorch圖像檢索實(shí)踐
隨著電子商務(wù)和在線網(wǎng)站的出現(xiàn),圖像檢索在我們的日常生活中的應(yīng)用一直在增加。
亞馬遜、阿里巴巴、Myntra等公司一直在大量利用圖像檢索技術(shù)。當(dāng)然,只有當(dāng)通常的信息檢索技術(shù)失敗時(shí),圖像檢索才會(huì)開(kāi)始工作。
背景
圖像檢索的基本本質(zhì)是根據(jù)查詢圖像的特征從集合或數(shù)據(jù)庫(kù)中查找圖像。
大多數(shù)情況下,這種特征是圖像之間簡(jiǎn)單的視覺(jué)相似性。在一個(gè)復(fù)雜的問(wèn)題中,這種特征可能是兩幅圖像在風(fēng)格上的相似性,甚至是互補(bǔ)性。
由于原始形式的圖像不會(huì)在基于像素的數(shù)據(jù)中反映這些特征,因此我們需要將這些像素?cái)?shù)據(jù)轉(zhuǎn)換為一個(gè)潛空間,在該空間中,圖像的表示將反映這些特征。
一般來(lái)說(shuō),在潛空間中,任何兩個(gè)相似的圖像都會(huì)相互靠近,而不同的圖像則會(huì)相隔很遠(yuǎn)。這是我們用來(lái)訓(xùn)練我們的模型的基本管理規(guī)則。一旦我們這樣做,檢索部分只需搜索潛在空間,在給定查詢圖像表示的潛在空間中拾取最近的圖像。大多數(shù)情況下,它是在最近鄰搜索的幫助下完成的。
因此,我們可以將我們的方法分為兩部分:
1. 圖像表現(xiàn)
2. 搜索
我們將在Oxford 102 Flowers數(shù)據(jù)集上解決這兩個(gè)部分。
圖像表現(xiàn)
我們將使用一種叫做暹羅模型的東西,它本身并不是一種全新的模型,而是一種訓(xùn)練模型的技術(shù)。大多數(shù)情況下,這是與triplet loss一起使用的。這個(gè)技術(shù)的基本組成部分是三元組。
三元組是3個(gè)獨(dú)立的數(shù)據(jù)樣本,比如A(錨點(diǎn)),B(陽(yáng)性)和C(陰性);其中A和B相似或具有相似的特征(可能是同一類(lèi)),而C與A和B都不相似。這三個(gè)樣本共同構(gòu)成了訓(xùn)練數(shù)據(jù)的一個(gè)單元——三元組。
注:任何圖像檢索任務(wù)的90%都體現(xiàn)在暹羅網(wǎng)絡(luò)、triplet loss和三元組的創(chuàng)建中。如果你成功地完成了這些,那么整個(gè)努力的成功或多或少是有保證的。
首先,我們將創(chuàng)建管道的這個(gè)組件——數(shù)據(jù)。下面我們將在PyTorch中創(chuàng)建一個(gè)自定義數(shù)據(jù)集和數(shù)據(jù)加載器,它將從數(shù)據(jù)集中生成三元組。
class TripletData(Dataset):
def __init__(self, path, transforms, split="train"):
self.path = path
self.split = split # train or valid
self.cats = 102 # number of categories
self.transforms = transforms
def __getitem__(self, idx):
# our positive class for the triplet
idx = str(idx%self.cats + 1)
# choosing our pair of positive images (im1, im2)
positives = os.listdir(os.path.join(self.path, idx))
im1, im2 = random.sample(positives, 2)
# choosing a negative class and negative image (im3)
negative_cats = [str(x+1) for x in range(self.cats)]
negative_cats.remove(idx)
negative_cat = str(random.choice(negative_cats))
negatives = os.listdir(os.path.join(self.path, negative_cat))
im3 = random.choice(negatives)
im1,im2,im3 = os.path.join(self.path, idx, im1), os.path.join(self.path, idx, im2), os.path.join(self.path, negative_cat, im3)
im1 = self.transforms(Image.open(im1))
im2 = self.transforms(Image.open(im2))
im3 = self.transforms(Image.open(im3))
return [im1, im2, im3]
# we'll put some value that we want since there can be far too many triplets possible
# multiples of the number of images/ number of categories is a good choice
def __len__(self):
return self.cats*8
# Transforms
train_transforms = transforms.Compose([
transforms.Resize((224,224)),
transforms.RandomHorizontalFlip(),
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
])
val_transforms = transforms.Compose([
transforms.Resize((224, 224)),
transforms.ToTensor(),
transforms.Normalize((0.4914, 0.4822, 0.4465), (0.2023, 0.1994, 0.2010)),
])
# Datasets and Dataloaders
train_data = TripletData(PATH_TRAIN, train_transforms)
val_data = TripletData(PATH_VALID, val_transforms)
train_loader = torch.utils.data.DataLoader(dataset = train_data, batch_size=32, shuffle=True, num_workers=4)
val_loader = torch.utils.data.DataLoader(dataset = val_data, batch_size=32, shuffle=False, num_workers=4)
現(xiàn)在我們有了數(shù)據(jù),讓我們轉(zhuǎn)到暹羅網(wǎng)絡(luò)。
暹羅網(wǎng)絡(luò)給人的印象是2個(gè)或3個(gè)模型,但是它本身是一個(gè)單一的模型。所有這些模型共享權(quán)重,即只有一個(gè)模型。
如前所述,將整個(gè)體系結(jié)構(gòu)結(jié)合在一起的關(guān)鍵因素是triplet loss。triplet loss產(chǎn)生了一個(gè)目標(biāo)函數(shù),該函數(shù)迫使相似輸入對(duì)(錨點(diǎn)和正)之間的距離小于不同輸入對(duì)(錨點(diǎn)和負(fù))之間的距離,并限定一定的閾值。
下面我們來(lái)看看triplet loss以及訓(xùn)練管道實(shí)現(xiàn)。
class TripletLoss(nn.Module):
def __init__(self, margin=1.0):
super(TripletLoss, self).__init__()
self.margin = margin
def calc_euclidean(self, x1, x2):
return (x1 - x2).pow(2).sum(1)
# Distances in embedding space is calculated in euclidean
def forward(self, anchor, positive, negative):
distance_positive = self.calc_euclidean(anchor, positive)
distance_negative = self.calc_euclidean(anchor, negative)
losses = torch.relu(distance_positive - distance_negative + self.margin)
return losses.mean()
device = 'cuda'
# Our base model
model = models.resnet18().cuda()
optimizer = optim.Adam(model.parameters(), lr=0.001)
triplet_loss = TripletLoss()
# Training
for epoch in range(epochs):
model.train()
epoch_loss = 0.0
for data in tqdm(train_loader):
optimizer.zero_grad()
x1,x2,x3 = data
e1 = model(x1.to(device))
e2 = model(x2.to(device))
e3 = model(x3.to(device))
loss = triplet_loss(e1,e2,e3)
epoch_loss += loss
loss.backward()
optimizer.step()
print("Train Loss: {}".format(epoch_loss.item()))
class TripletLoss(nn.Module):
def __init__(self, margin=1.0):
super(TripletLoss, self).__init__()
self.margin = margin
def calc_euclidean(self, x1, x2):
return (x1 - x2).pow(2).sum(1)
# Distances in embedding space is calculated in euclidean
def forward(self, anchor, positive, negative):
distance_positive = self.calc_euclidean(anchor, positive)
distance_negative = self.calc_euclidean(anchor, negative)
losses = torch.relu(distance_positive - distance_negative + self.margin)
return losses.mean()
device = 'cuda'
# Our base model
model = models.resnet18().cuda()
optimizer = optim.Adam(model.parameters(), lr=0.001)
triplet_loss = TripletLoss()
# Training
for epoch in range(epochs):
model.train()
epoch_loss = 0.0
for data in tqdm(train_loader):
optimizer.zero_grad()
x1,x2,x3 = data
e1 = model(x1.to(device))
e2 = model(x2.to(device))
e3 = model(x3.to(device))
loss = triplet_loss(e1,e2,e3)
epoch_loss += loss
loss.backward()
optimizer.step()
print("Train Loss: {}".format(epoch_loss.item()))
到目前為止,我們的模型已經(jīng)經(jīng)過(guò)訓(xùn)練,可以將圖像轉(zhuǎn)換為一個(gè)嵌入空間。接下來(lái),我們進(jìn)入搜索部分。
搜索
我們可以很容易地使用Scikit Learn提供的最近鄰搜索。我們將探索新的更好的東西,而不是走簡(jiǎn)單的路線。
我們將使用Faiss。這比最近的鄰居要快得多,如果我們有大量的圖像,這種速度上的差異會(huì)變得更加明顯。
下面我們將演示如何在給定查詢圖像時(shí),在存儲(chǔ)的圖像表示中搜索最近的圖像。
#!pip install faiss-gpu
import faiss
faiss_index = faiss.IndexFlatL2(1000) # build the index
# storing the image representations
im_indices = []
with torch.no_grad():
for f in glob.glob(os.path.join(PATH_TRAIN, '*')):
im = Image.open(f)
im = im.resize((224,224))
im = torch.tensor([val_transforms(im).numpy()]).cuda()
preds = model(im)
preds = np.a(chǎn)rray([preds[0].cpu().numpy()])
faiss_index.a(chǎn)dd(preds) #add the representation to index
im_indices.a(chǎn)ppend(f) #store the image name to find it later on
# Retrieval with a query image
with torch.no_grad():
for f in os.listdir(PATH_TEST):
# query/test image
im = Image.open(os.path.join(PATH_TEST,f))
im = im.resize((224,224))
im = torch.tensor([val_transforms(im).numpy()]).cuda()
test_embed = model(im).cpu().numpy()
_, I = faiss_index.search(test_embed, 5)
print("Retrieved Image: {}".format(im_indices[I[0][0]]))
這涵蓋了基于現(xiàn)代深度學(xué)習(xí)的圖像檢索,但不會(huì)使其變得太復(fù)雜。大多數(shù)檢索問(wèn)題都可以通過(guò)這個(gè)基本管道解決。
原文標(biāo)題 : Pytorch圖像檢索實(shí)踐
發(fā)表評(píng)論
請(qǐng)輸入評(píng)論內(nèi)容...
請(qǐng)輸入評(píng)論/評(píng)論長(zhǎng)度6~500個(gè)字
最新活動(dòng)更多
-
10月31日立即下載>> 【限時(shí)免費(fèi)下載】TE暖通空調(diào)系統(tǒng)高效可靠的組件解決方案
-
即日-11.13立即報(bào)名>>> 【在線會(huì)議】多物理場(chǎng)仿真助跑新能源汽車(chē)
-
11月28日立即報(bào)名>>> 2024工程師系列—工業(yè)電子技術(shù)在線會(huì)議
-
12月19日立即報(bào)名>> 【線下會(huì)議】OFweek 2024(第九屆)物聯(lián)網(wǎng)產(chǎn)業(yè)大會(huì)
-
即日-12.26火熱報(bào)名中>> OFweek2024中國(guó)智造CIO在線峰會(huì)
-
即日-2025.8.1立即下載>> 《2024智能制造產(chǎn)業(yè)高端化、智能化、綠色化發(fā)展藍(lán)皮書(shū)》
推薦專(zhuān)題
- 1 【一周車(chē)話】沒(méi)有方向盤(pán)和踏板的車(chē),你敢坐嗎?
- 2 特斯拉發(fā)布無(wú)人駕駛車(chē),還未迎來(lái)“Chatgpt時(shí)刻”
- 3 特斯拉股價(jià)大跌15%:Robotaxi離落地還差一個(gè)蘿卜快跑
- 4 馬斯克給的“驚喜”夠嗎?
- 5 海信給AI電視打樣,12大AI智能體全面升級(jí)大屏體驗(yàn)
- 6 打完“價(jià)格戰(zhàn)”,大模型還要比什么?
- 7 馬斯克致敬“國(guó)產(chǎn)蘿卜”?
- 8 神經(jīng)網(wǎng)絡(luò),誰(shuí)是盈利最強(qiáng)企業(yè)?
- 9 比蘋(píng)果偉大100倍!真正改寫(xiě)人類(lèi)歷史的智能產(chǎn)品降臨
- 10 諾獎(jiǎng)進(jìn)入“AI時(shí)代”,人類(lèi)何去何從?
- 高級(jí)軟件工程師 廣東省/深圳市
- 自動(dòng)化高級(jí)工程師 廣東省/深圳市
- 光器件研發(fā)工程師 福建省/福州市
- 銷(xiāo)售總監(jiān)(光器件) 北京市/海淀區(qū)
- 激光器高級(jí)銷(xiāo)售經(jīng)理 上海市/虹口區(qū)
- 光器件物理工程師 北京市/海淀區(qū)
- 激光研發(fā)工程師 北京市/昌平區(qū)
- 技術(shù)專(zhuān)家 廣東省/江門(mén)市
- 封裝工程師 北京市/海淀區(qū)
- 結(jié)構(gòu)工程師 廣東省/深圳市