التعلم العميقالمزيد من مقالات التعلم العميق......مقالات التعلم العميق

الشبكات العصبية السيامية

(لا تحتاج دائمًا إلى الكثير من البيانات لتدريب نموذجك، وتعلم كيفية إنشاء نموذج بعدد ضئيل من الصور لكل فئة)

في عصر التعلم العميق الحديث، تعد الشبكات العصبية جيدة تقريبًا في كل مهمة، لكن هذه الشبكات العصبية تعتمد على المزيد من البيانات لأداء جيد. ولكن، بالنسبة لبعض المشكلات مثل التعرف على الوجوه والتحقق من التوقيع، لا يمكننا دائمًا الاعتماد على الحصول على المزيد من البيانات، لحل هذا النوع من المهام، لدينا نوع جديد من بنية الشبكات العصبية تسمى الشبكات السيامية Siamese Networks.

يستخدم فقط عددًا قليلاً من الصور للحصول على تنبؤات أفضل. جعلت القدرة على التعلم من القليل جدًا من البيانات الشبكات السيامية أكثر شعبية في السنوات الأخيرة. في هذه المقالة، سوف نستكشف ما هو عليه وكيفية تطوير نظام التحقق من التوقيع باستخدام Pytorch باستخدام الشبكات السيامية.

ما هي الشبكات السيامية!؟

 

الشبكة العصبية السيامية (SNN) Siamese Neural Network هي فئة من بنى الشبكات العصبية التي تحتوي على شبكتين فرعيتين متطابقتين أو أكثر. تعني كلمة “متطابقة identical” هنا، أن لديهم نفس التكوين مع نفس المعلمات والأوزان. يتم عكس تحديث المعلمة عبر كلتا الشبكتين الفرعيتين. يتم استخدامه للعثور على تشابه المدخلات من خلال مقارنة متجهات الميزات الخاصة بها، لذلك يتم استخدام هذه الشبكات في العديد من التطبيقات

تقليديا، تتعلم الشبكة العصبية التنبؤ بفئات متعددة. هذا يطرح مشكلة عندما نحتاج إلى إضافة / إزالة فئات جديدة إلى البيانات. في هذه الحالة، يتعين علينا تحديث الشبكة العصبية وإعادة تدريبها على مجموعة البيانات بأكملها. أيضًا، تحتاج الشبكات العصبية العميقة إلى كمية كبيرة من البيانات للتدريب عليها. من ناحية أخرى، تتعلم SNNs دالة التشابه similarity function. وبالتالي، يمكننا تدريبها لمعرفة ما إذا كانت الصورتان متماثلتان (وهو ما سنفعله هنا). وهذا يمكننا من تصنيف فئات جديدة من البيانات دون تدريب الشبكة مرة أخرى.

إيجابيات وسلبيات الشبكات السيامية

الإيجابيات الرئيسية للشبكات السيامية هي:

  • أكثر قوة في الفئات غير المتوازنة: بمساعدة التعلم دفعة واحدة One-shot learning، فإن إعطاء عدد قليل من الصور لكل فئة يكفي للشبكات السيامية للتعرف على تلك الصور في المستقبل.
  • جيد بالنسبة للتجميع مع أفضل مصنف: نظرًا لأن آلية التعلم الخاصة بها تختلف إلى حد ما عن التصنيف، فإن حساب المتوسط البسيط لها باستخدام المصنف يمكن أن يكون أفضل بكثير من متوسط اثنين من النماذج الخاضعة للإشراف (مثل مصنف GBM و RF)
  • التعلم من التشابه الدلالي: تركز الشبكات السيامية على تضمينات التعلم learning embeddings (في الطبقة الأعمق) التي تضع نفس الفئات / المفاهيم قريبة من بعضها البعض. وبالتالي، يمكن تعلم التشابه الدلالي semantic similarity.

يمكن أن تكون سلبيات الشبكات السيامية:

  • يحتاج إلى وقت تدريب أطول من الشبكات العادية: نظرًا لأن الشبكات السيامية تتضمن أزواجًا تربيعية للتعلم منها (لرؤية جميع المعلومات المتاحة) فهي أبطأ من نوع التصنيف العادي للتعلم (التعلم النقطي pointwise learning)
  • لا ينتج احتمالات: نظرًا لأن التدريب يتضمن التعلم الزوجي pairwise learning، فلن ينتج عنه احتمالات التنبؤ.

دوال الخطأ المستخدمة في الشبكات السيامية

نظرًا لأن تدريب الشبكات السيامية يتضمن التعلم الزوجي المعتاد، فلا يمكن استخدام خطأ الانتروبيا المتقاطعة Cross entropy loss في هذه الحالة، يتم استخدام دالتين أساسيتين للخطأ بشكل أساسي في تدريب هذه الشبكات السيامية.

الخطأ الثلاثي هي دالة خطأ حيث تتم مقارنة مدخلات خط الأساس baseline (المرساة anchor) بإدخال إيجابي positive (حقيقي truthy) وادخال سلبي negative (زائف falsy). يتم تقليل المسافة من إدخال الخط الأساسي (المرساة) إلى المدخلات الإيجابية (الحقيقية) إلى الحد الأدنى، ويتم تكبير المسافة من إدخال خط الأساس (المرساة) إلى المدخلات السلبية (الزائفة).

في المعادلة أعلاه، ألفا alpha عبارة عن مصطلح هامشي يستخدم “لتمديد stretch” فروق المسافة بين الأزواج المتشابهة وغير المتشابهة في الثلاثي ، fa ، fp ، fn هي ميزة التضمين للصور المرساة والموجبة والسلبية.

أثناء عملية التدريب، يتم إدخال صورة ثلاثية (صورة مرساة، صورة سلبية، صورة إيجابية) (صورة مرساة، صورة سلبية، صورة إيجابية) في النموذج كعينة واحدة. الفكرة من وراء ذلك هي أن المسافة بين الصور المرساة والصور الإيجابية يجب أن تكون أصغر من تلك بين صورة المرساة والصور السلبية.

الخطأ التبايني Contrastive Los: هي دالة خطأ شائعة مستخدمة بشكل كبير في الوقت الحاضر، وهي خطأ قائمة على المسافة بدلاً من الاخطاء التقليدية للتنبؤ بالأخطاء. يستخدم هذا الخطأ لتعلم التضمينات التي يكون فيها نقطتان متشابهتان لهما مسافة إقليدية منخفضة ونقطتان مختلفتان لهما مسافة إقليدية كبيرة.

وعرّفنا Dw وهي المسافة الإقليدية على النحو التالي:

Gw هو ناتج شبكتنا لصورة واحدة.

التحقق من التوقيع مع الشبكات السيامية

نظرًا لأن الشبكات السيامية تُستخدم في الغالب في أنظمة التحقق مثل التعرف على الوجوه والتحقق من التوقيع وما إلى ذلك … فلننفذ نظام التحقق من التوقيع باستخدام الشبكات العصبية السيامية على Pytorch.

مجموعة البيانات والمعالجة المسبقة لمجموعة البيانات

سنستخدم مجموعة بيانات ICDAR 2011 التي تتكون من توقيعات المستخدمين الهولنديين الأصيلة genuine  والمزورة fraud على حد سواء، ومجموعة البيانات نفسها مفصولة كتدريب ومجلدات ، داخل كل مجلد ، تتكون من مجلد مستخدمين مفصول على أنه أصلي ومزور ، أيضًا تتوفر تسميات مجموعة البيانات كملفات CSV ، يمكنك تنزيل مجموعة البيانات من هنا.

الآن لإدخال هذه البيانات الأولية في شبكتنا العصبية، يتعين علينا تحويل جميع الصور إلى موترات tensors وإضافة التسميات labels من ملفات CSV إلى الصور، للقيام بذلك يمكننا استخدام فئة مجموعة البيانات المخصصة من Pytorch ، وإليك كيفية عملنا الكامل سيبدو الكود:

#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
        img0 = Image.open(image1_path)
        img1 = Image.open(image2_path)
        img0 = img0.convert("L")
        img1 = img1.convert("L")
        # Apply image transformations
        if self.transform is not None:
            img0 = self.transform(img0)
            img1 = self.transform(img1)
        return img0, img1 , th.from_numpy(np.array([int(self.train_df.iat[index,2])],dtype=np.float32))
    def __len__(self):
        return len(self.train_df)

الآن بعد المعالجة المسبقة لمجموعة البيانات ، في PyTorch ، يتعين علينا تحميل مجموعة البيانات باستخدام فئة Dataloader ، وسنستخدم دالة التحويلات transforms function لتقليل حجم الصورة إلى 105 بكسل من الارتفاع والعرض لأغراض حسابية.

# Load the the dataset from raw image folders
siamese_dataset = SiameseDataset(training_csv,training_dir,
                                        transform=transforms.Compose([transforms.Resize((105,105)),
                                                                      transforms.ToTensor()
                                                                      ])
                                       )

بُنية الشبكة العصبية

دعنا الآن ننشئ شبكة عصبية في Pytorch، سنستخدم بُنية الشبكة العصبية التي ستكون متشابهة، كما هو موضح في مقالة Signet.

#create a siamese network
class SiameseNetwork(nn.Module):
    def __init__(self):
        super(SiameseNetwork, self).__init__()
        # Setting up the Sequential of CNN Layers
        self.cnn1 = 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.fc1 = 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.cnn1(x)
        output = output.view(output.size()[0], -1)
        output = self.fc1(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

في الكود أعلاه، أنشأنا شبكتنا على النحو التالي، تقوم الطبقات التلافيفية convolutional layers الأولى بتصفية صورة توقيع الإدخال 105 * 105 مع 96 نواة بحجم 11 بخطوة 1 بكسل. تأخذ الطبقة التلافيفية الثانية كمدخل إخراج (مطبع normalized ومجمعة normalized ) للطبقة التلافيفية الأولى وترشحها بـ 256 نواة من الحجم 5. ترتبط الطبقتان التلافيفيتان الثالثة والرابعة ببعضهما البعض دون أي تدخل من تجميع أو تسوية طبقات. تحتوي الطبقة الثالثة على 384 نواة من الحجم 3 متصلة بإخراج (مطبع normalized، مجمّع pooled، متسرب dropout) للطبقة التلافيفية الثانية. تحتوي الطبقة التلافيفية الرابعة على 256 نواة من الحجم 3 وهذا يؤدي إلى تعلم الشبكة العصبية عددًا أقل من ميزات المستوى الأدنى للحقول الاستقبالية receptive fields الأصغر والمزيد من الميزات للحصول على ميزات ذات مستوى أعلى أو أكثر تجريدًا. تحتوي الطبقة الأولى المتصلة fully connected بالكامل على 1024 خلية عصبية، بينما تحتوي الطبقة الثانية المتصلة بالكامل على 128 خلية عصبية. يشير هذا إلى أن أعلى متجه للميزات تم تعلمه من كل جانب من جوانب SigNet له بُعد يساوي 128، فأين الشبكة الأخرى؟

نظرًا لأن الأوزان مقيدة لتكون متطابقة لكلتا الشبكتين، فإننا نستخدم نموذجًا واحدًا ونغذيه بصورتين على التوالي. بعد ذلك، نحسب قيمة الخطأ باستخدام كل من الصور ثم الانتشار الخلفي backpropagate. هذا يوفر الكثير من الذاكرة وكذلك الكفاءة الحسابية.

دالة الخطأ

لهذه المهمة، سوف نستخدم الخطأ التبايني، والتي تتعلم التضمينات التي يكون فيها نقطتان متشابهتان لهما مسافة إقليدية منخفضة ونقطتان مختلفتان لهما مسافة إقليدية كبيرة ، في Pytorch سيكون تنفيذ الخطأ المتباين على النحو التالي:

class ContrastiveLoss(torch.nn.Module):
    """
    Contrastive loss function.
    Based on:
    """

    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

تدريب الشبكة

تكون عملية تدريب الشبكة السيامية كما يلي:

  • تهيئة الشبكة ودالة الخطأ والمحسن (سنستخدم Adam لهذا المشروع).
  • تمرير الصورة الأولى من ازواج الصور عبر الشبكة.
  • تمرير الصورة الثانية من ازواج الصور عبر الشبكة.
  • حساب الخطأ باستخدام مخرجات الصورتين الأولى والثانية.
  • إعادة نشر الخطأ لحساب التدرجات في نموذجنا.
  • تحديث الأوزان باستخدام المُحسِّن.
  • حفظ النموذج.
# 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(), "model.pt")
print("Model Saved Successfully") 

تم تدريب النموذج لمدة 20 حقبة على google colab لمدة ساعة، ويظهر الرسم البياني للخطأ بمرور الوقت أدناه.

اختبار النموذج

الآن دعنا نختبر نظام التحقق من التوقيع الخاص بنا على مجموعة بيانات الاختبار:

  • تحميل مجموعة بيانات الاختبار باستخدام فئة DataLoader من Pytorch.
  • تمريرأزواج الصور والتسميات؟
  • ايجاد المسافة الإقليدية بين الصور.
  • بناءً على المسافة الإقليدية، قم بطباعة الإخراج.
# 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

كانت التوقعات على النحو التالي:

الاستنتاج

في هذه المقالة، ناقشنا كيف تختلف الشبكات السيامية عن شبكات التعلم العميق العادية وطبقت نظام التحقق من التوقيع باستخدام الشبكات السيامية، يمكنك العثور على الكود بالكامل هنا.

بواسطة
Sean Benhur
المصدر
A friendly introduction to Siamese Networks

د. علاء طعيمة

كلية علوم الحاسوب وتكنولوجيا المعلومات / جامعة القادسية / العراق

مقالات ذات صلة

اترك تعليقاً

لن يتم نشر عنوان بريدك الإلكتروني. الحقول الإلزامية مشار إليها بـ *

زر الذهاب إلى الأعلى