Ngày nay, AI đã len lỏi vào mọi lĩnh vực của đời sống xã hội: y tế, giáo dục, giao thông, … Có thể nói không ngoa rằng hầu như mọi bài toán AI đều có thể giải quyết được thông qua Neural Network (NN). Tuy nhiên, các NNs luôn đòi hỏi lượng lớn dữ liệu để huấn luyện chúng. Trong thực tế, có một số bài toán mà việc thu thập đủ dữ liệu là một nhiệm vụ bất khả thi, ví dụ như bài toán Face Recognition, Signature Verification, … Siamese Network (SN) hay Siamese Neural Network (SNN) ra đời để giải quyết tốt hơn những bài toán dạng như thế này.
SNN chỉ sử dụng một số lượng hình ảnh rất nhỏ (vài ảnh) để có được những dự đoán tốt hơn nhiều so với mạng NN truyền thống. Vì thế nó còn được gọi với 1 số cái tên như One-shot Learning, Few-shot Learning, … Khả năng học hỏi từ rất ít dữ liệu đã khiến cho SNN trở nên phổ biến hơn trong những năm gần đây. Trong bài viết này, chúng ta sẽ tìm hiểu nó là gì và cách phát triển hệ thống Signature Verification với Pytorch bằng cách sử dụng SNN.
1. Giới thiệu Siamese Neural Network
Siamese Neural Network (SNN) là một kiến trúc mạng nơ-ron chứa hai hoặc nhiều mạng con giống hệt nhau. “Giống hệt nhau” ở đây có nghĩa là, chúng có cùng cấu hình với cùng thông số và trọng số. Việc cập nhật các thông số được phản ánh đồng thời trên cả hai mạng con của nó.
SNN được sử dụng để tìm sự giống nhau của các dữ liệu đầu (Input Data) vào bằng cách so sánh các vectơ đặc trưng của chúng. Một số ứng dụng phổ biến của SNN có thể kể đến như là: Face Verification, Signature Verification, Image Seaching System, …
Thông thường, một mạng nơ-ron học cách để dự đoán các lớp của một bài toán. Nếu muốn thêm hay bớt các lớp mới, chúng ta phải cập nhật (huấn luyện) lại mạng nơ-ron trên toàn bộ tập dữ liệu (cả dữ liệu mới và cũ). Ngoài ra, các mạng nơ-ron sâu cần một khối lượng lớn dữ liệu để có thể huấn luyện chúng. SNN, theo một cách khác, học cách tìm ra sự giống nhau giữa các Input Data. Vì vậy, nó cho phép chúng ta phân loại các lớp dữ liệu mới mà không cần huấn luyện lại mạng nơ-ron.
Luồng làm việc của SNN như sau:
2. Ưu điểm của SNN
SNN có một số ưu điểm nổi bật như sau:
Lượng dữ liệu cần thiết để huấn luyện SNN là rất ít. Chỉ cần vài Samples là đủ (1-5 samples) huấn luyện SNN. Phương pháp mà nó sử dụng ở đây là One-Shot Learning hoặc Few-Shot Learning. Chính vì cần ít dữ liệu huấn luyện như vậy nên chúng ta cũng không lo lắng việc dữ liệu bị mất cân bằng (Image Imbalance).
Khả năng kết hợp với các bộ phân loại khác cao. Do cơ chế học của SNN khác biệt với các bộ phân lớp thông thường khác, nên chúng ta hoàn toàn có thể kết hợp chúng lại với nhau. Việc làm này thường cho ra kết quả tốt hơn.
Học từ sự tương đồng về ngữ nghĩa: SNN tập trung vào việc học các Features ở các lớp sâu hơn, nơi mà các Features giống nhau được đặt gần nhau. Do đó, nó có thể hiểu được phần nào sự tương đồng về ngữ nghĩa của các Input Data.
3. Nhược điểm của SNN
SNN cũng có những nhược điểm sau:
Thời gian huấn luyện lâu hơn. SNN học theo từng cặp đôi một với nhau nên khả năng học của nó chậm hơn các NN khác.
Không thể hiện xác suất mỗi lớp trong Output. SNN chỉ đưa đưa 1 giá trị Score trong đoạn [0,1], thể hiện sự giống nhau giữa 2 Input Data. Score càng gần 1 thì 2 Input Data càng giống nhau và ngược lại.
4. Loss Function của SNN
Bởi vì, SNN học theo kiểu từng đôi một của Input Data nên Cross Entropy Loss Function thường không được sử dụng. Thay vào đó, 2 Loss Functions là Triple Loss và Contrastive Loss được sử dụng nhiều hơn.
4.1 Triple Loss function
Ý tưởng của Triple Loss là sử dụng bộ 3 Input Data bao gồm: Anchor (A), Positive (P) và Nagative (N) mà ở đó, khoảng cách từ A đến P được tối thiểu hóa, trong khi khoảng cách từ A đến N được tối đa hóa trong suốt quá trình huấn luyện model.
$L(A,P,N) = max(||f(A) - f(P)||^2 - ||f(A) - f(N)||^2 + \alpha,0)$
Trong công thức trên,
4.2 Contrastive Loss
Ý tưởng của Contrastive Loss cũng tương tự như Triplet Loss, sự khác nhau ở chỗ Contrastive Loss chỉ sử dụng 1 cặp Input Data, hoặc là cùng loại, hoặc là khác loại. Nếu cùng loại thì khoảng cách giữa các vectors đặc trưng của chúng sẽ được tối thiểu hóa, còn nếu khác loại thì khoảng cách giữa các vectors đặc trưng của chúng sẽ được tối đa hóa trong suốt quá trình huấn luyện.
Công thức của Contrastive Loss:
$(1 - Y)\frac{1}{2}(D_w)^2 + (Y)\frac{1}{2}{max(0,m - D_w)}^2$
Trong đó, $D_w$ là khoảng cách Euclidean:
$\sqrt{{G_w(X_1) - G_w(X_2)}^2}$
$G_w$ là Ouput của SNN đối với 1 Input Data.
Việc lựa chọn sử dụng Loss Function nào còn tùy thuộc vào bài toán cụ thể. Chưa có công bố nào kết luận cái nào tốt hơn cái nào. Bạn nên thử cả 2 loại để tìm ra cái tốt hơn cho bài toán của bạn.
5. Signature Verification với Siamese Networks
Trong phần này, chúng ta sẽ xây dựng một SNN model bằng Pytorch để thực hiện nhiệm vụ Signature Verification.
5.1 Signature dataset
Dataset sử dụng trong bài này là ICDAR 2011. Nó chứa các chữ ký của những người dân ở Hà Lan, cả chữ ký thật và chữ ký giả. Nhãn của dữ liệu nằm trong file CSV tương ứng.
Chúng ta sẽ đoc vào dataset và chuẩn bị cho việc huấn luyện model:
#preprocessing and loading the dataset
class SiameseDataset():
def __init__(self,training_csv=None,training_dir=None,transform=None):
# used to prepare the labels and images path
self.train_df=pd.read_csv(training_csv)
self.train_df.columns =["image1","image2","label"]
self.train_dir = training_dir
self.transform = transform
def __getitem__(self,index):
# getting the image path
image1_path=os.path.join(self.train_dir,self.train_df.iat[index,0])
image2_path=os.path.join(self.train_dir,self.train_df.iat[index,1])
# Loading the image
img1 = Image.open(image1_path)
img2 = Image.open(image2_path)
img1 = img0.convert("L")
img2 = img1.convert("L")
# Apply image transformations
if self.transform is not None:
img1 = self.transform(img1)
img2 = self.transform(img2)
return img1, img2 , th.from_numpy(np.array([int(self.train_df.iat[index,2])],dtype=np.float32))
def __len__(self):
return len(self.train_df)
# Load the the dataset from raw image folders
siamese_dataset = SiameseDataset(training_csv,training_dir,
transform=transforms.Compose([transforms.Resize((105,105)),
transforms.ToTensor()
])
)
# Load the dataset as pytorch tensors using dataloader
train_dataloader = DataLoader(
siamese_dataset, shuffle=True, num_workers=8, batch_size=config.batch_size
)
5.2 SNN model
Chúng ta sẽ tạo SNN model như sau:
#create a siamese network
class SiameseNetwork(nn.Module):
def __init__(self):
super(SiameseNetwork, self).__init__()
# Setting up the Sequential of CNN Layers
self.cnn = nn.Sequential(
nn.Conv2d(1, 96, kernel_size=11,stride=1),
nn.ReLU(inplace=True),
nn.LocalResponseNorm(5,alpha=0.0001,beta=0.75,k=2),
nn.MaxPool2d(3, stride=2),
nn.Conv2d(96, 256, kernel_size=5,stride=1,padding=2),
nn.ReLU(inplace=True),
nn.LocalResponseNorm(5,alpha=0.0001,beta=0.75,k=2),
nn.MaxPool2d(3, stride=2),
nn.Dropout2d(p=0.3),
nn.Conv2d(256,384 , kernel_size=3,stride=1,padding=1),
nn.ReLU(inplace=True),
nn.Conv2d(384,256 , kernel_size=3,stride=1,padding=1),
nn.ReLU(inplace=True),
nn.MaxPool2d(3, stride=2),
nn.Dropout2d(p=0.3),
)
# Defining the fully connected layers
self.fc = nn.Sequential(
nn.Linear(30976, 1024),
nn.ReLU(inplace=True),
nn.Dropout2d(p=0.5),
nn.Linear(1024, 128),
nn.ReLU(inplace=True),
nn.Linear(128,2))
def forward_once(self, x):
# Forward pass
output = self.cnn(x)
output = output.view(output.size()[0], -1)
output = self.fc(output)
return output
def forward(self, input1, input2):
# forward pass of input 1
output1 = self.forward_once(input1)
# forward pass of input 2
output2 = self.forward_once(input2)
return output1, output2
5.3 Loss Function
Trong bài này, mình sẽ sử dụng Contrastive Loss. Code của nó trong Pytorch như sau:
class ContrastiveLoss(torch.nn.Module):
"""
Contrastive loss function.
"""
def __init__(self, margin=1.0):
super(ContrastiveLoss, self).__init__()
self.margin = margin
def forward(self, x0, x1, y):
# euclidian distance
diff = x0 - x1
dist_sq = torch.sum(torch.pow(diff, 2), 1)
dist = torch.sqrt(dist_sq)
mdist = self.margin - dist
dist = torch.clamp(mdist, min=0.0)
loss = y * dist_sq + (1 - y) * torch.pow(dist, 2)
loss = torch.sum(loss) / 2.0 / x0.size()[0]
return loss
5.4 Train SNN model
Các bước tiến hành huấn luyện SNN model như sau:
# Declare Siamese Network
net = SiameseNetwork().cuda()
# Decalre Loss Function
criterion = ContrastiveLoss()
# Declare Optimizer
optimizer = th.optim.Adam(net.parameters(), lr=1e-3, weight_decay=0.0005)
#train the model
def train():
loss=[]
counter=[]
iteration_number = 0
for epoch in range(1,config.epochs):
for i, data in enumerate(train_dataloader,0):
img0, img1 , label = data
img0, img1 , label = img0.cuda(), img1.cuda() , label.cuda()
optimizer.zero_grad()
output1,output2 = net(img0,img1)
loss_contrastive = criterion(output1,output2,label)
loss_contrastive.backward()
optimizer.step()
print("Epoch {}\n Current loss {}\n".format(epoch,loss_contrastive.item()))
iteration_number += 10
counter.append(iteration_number)
loss.append(loss_contrastive.item())
show_plot(counter, loss)
return net
#set the device to cuda
device = torch.device('cuda' if th.cuda.is_available() else 'cpu')
model = train()
torch.save(model.state_dict(), "output/model.pt")
print("Model Saved Successfully")
Kết quả huấn luyện sau 20 epochs:
Giá trị của Loss vẫn còn dao động, có lẽ chúng ta phải Tuning model nhiều hơn. Trong bài này, mình ko đi chi tiết phần đó.
5.5 Test SNN model
Chúng ta sẽ thực hiện các bước sau để kiểm tra SNN model vừa mới huấn luyện:
# Load the test dataset
test_dataset = SiameseDataset(training_csv=testing_csv,training_dir=testing_dir,
transform=transforms.Compose([transforms.Resize((105,105)),
transforms.ToTensor()
])
)
test_dataloader = DataLoader(test_dataset,num_workers=6,batch_size=1,shuffle=True)
#test the network
count=0
for i, data in enumerate(test_dataloader,0):
x0, x1 , label = data
concat = torch.cat((x0,x1),0)
output1,output2 = model(x0.to(device),x1.to(device))
eucledian_distance = F.pairwise_distance(output1, output2)
if label==torch.FloatTensor([[0]]):
label="Original Pair Of Signature"
else:
label="Forged Pair Of Signature"
imshow(torchvision.utils.make_grid(concat))
print("Predicted Eucledian Distance:-",eucledian_distance.item())
print("Actual Label:-",label)
count=count+1
if count ==10:
break
Kết quả:
Predicted Eucledian Distance:- 1.2930774688720703
Actual Label:- Forged Pair Of Signature
Predicted Eucledian Distance:- 0.6725202798843384
Actual Label:- Original Pair Of Signature
Predicted Eucledian Distance:- 0.8823959827423096
Actual Label:- Forged Pair Of Signature
Predicted Eucledian Distance:- 0.9346675276756287
Actual Label:- Forged Pair Of Signature
Predicted Eucledian Distance:- 0.25577670335769653
Actual Label:- Forged Pair Of Signature
Predicted Eucledian Distance:- 0.7937518358230591
Actual Label:- Forged Pair Of Signature
Predicted Eucledian Distance:- 0.7733522057533264
Actual Label:- Original Pair Of Signature
Predicted Eucledian Distance:- 0.7810924649238586
Actual Label:- Original Pair Of Signature
Predicted Eucledian Distance:- 1.2326889038085938
Actual Label:- Original Pair Of Signature
Predicted Eucledian Distance:- 0.6290231347084045
Actual Label:- Original Pair Of Signature
Một số hình ảnh:
6. Kết luận
Trong bài này, chúng ta đã cùng tìm hiểu về Siamese Neural Network, đồng thời xây dựng một SNN đơn giản để giải quyết bài toán Signature Verification.
Toàn bộ source code của bài này, các bạn có thể tham khảo tại đây
Trong bài tiếp theo, chúng ta sẽ cùng thảo luận về cách thức xây dựng và tổ chức Source Code trong các dự án về AI. Mời các bạn đón đọc.
7. Tham khảo