لقد رأينا في المقال السابق كيفية إنشاء روبوت محادثة باستخدام Dialogflow وكيفية إضافة ميزات متنوعة للتعامل مع الكيانات والسياق المعقد. الآن ، نريد التعمق في جانب تعلم الألة في الأجزاء الداخلية لنظام الحوار. كما ناقشنا أثناء وصف خط الإنتاج لنظام الحوار ، فإن فهم السياق (أي استجابة المستخدم) في ضوء سجل المحادثة هو أحد أهم المهام لبناء نظام حوار.
يمكن تقسيم فهم السياق إلى فهم مقصد المستخدم واكتشاف الكيانات المقابلة لهذا الغرض المحدد. تتوافق هذه المكونات الداخلية مع مكون فهم اللغة الطبيعية في خط إنتاج روبوتات المحادثة . لتوضيح ذلك ، سنستعرض عينة من محادثة حول حجز المطعم وسنصف كيفية تصميم مكونات مختلفة لفهم السياق.
يوضح الشكل أدناه مثالاً لمستخدم يبحث عن حجز مطعم. كما نرى ، هناك تسميات متاحة لكل إجابة. تشير التسميات إلى المقاصد (intents ) والكيانات (entities ) لهذه الاستجابات. نريد استخدام هذه التعليقات التوضيحية لتدريب نماذج تعلم الألة الخاصة بنا.
قبل أن ندخل في النموذج ، سنحدد رسميًا مهمتين طبيعيتين للفهم تتعلقان بفهم سياق الحوارات. نظرًا لأن هذا يتضمن فهم الفروق الدقيقة في اللغة تحتها ، تُنسب هذه أيضًا إلى مهام فهم اللغة الطبيعية (natural language understanding) أو (NLU).
تصنيف نمط الحوار
تصنيف نمط الحوار(Dialog Act) هو مهمة لتحديد كيف يلعب نطق المستخدم دورًا في سياق الحوار. هذا يعرف بـ “النمط” الذي يقوم به المستخدم. على سبيل المثال ، من الأمثلة البسيطة على أنماط الحوار تحديد سؤال “نعم / لا”. إذا سأل المستخدم ، “هل ستذهب إلى المدرسة اليوم؟” ، فسيتم تصنيف هذا على أنه سؤال نعم / لا.
من ناحية أخرى ، إذا سأل المستخدم “ما هو عمق المحيط؟” ، فقد لا يتم تصنيف ذلك على أنه سؤال بنعم / لا. لقد رأينا أن المقاصد أو أنماط الحوار مهمة لبناء روبوت محادثة ، حتى في Cloud APIs. يساعد تحديد النية على فهم ما يطلبه المستخدم واتخاذ الإجراءات وفقًا لذلك.
يمكن إعادة صياغة هذا باعتباره مشكلة تصنيف: بالنظر إلى الكلام المنطوق في الحوار ، قم بتصنيفه إلى أنماط حوار أو تسميات. في مثالنا من الشكل أعلاه ، نحدد مهمة توقع إجراء حوار حيث تتضمن التسميات إعلام وطلبًا وما إلى ذلك.
العبارة المنطوقة “Where is it” يمكن تصنيفها على أنه نمط حوارا “طلب”. من ناحية أخرى ، يمكن تصنيف العبارة “I’m looking for a cheaper restaurant ” على أنها نمط حوار “إبلاغ”.
تحديد الخانات (Identifying Slots)
بمجرد استخراج المقاصد ، نريد الانتقال إلى استخراج الكيانات. يعد استخراج الكيانات مهمًا أيضًا لإنشاء استجابات صحيحة ومناسبة لإدخال المستخدم. لقد رأينا في المقال السابق في مثال Dialogflow أن استخراج الكيانات جنبًا إلى جنب مع المقاصد يخلق فهمًا كاملاً لإدخال المستخدم.
في المثال الموضح بالشكل أعلاه – “I’m looking for a cheaper restaurant” – نريد تحديد “أرخص” على أنه شريحة أسعار ونأخذ قيمتها حرفياً – أي أن قيمة الشريحة “أرخص”. لقد رأينا مهامًا مماثلة في هذا المقال ، حيث تعلمنا كيفية استخراج الكيانات من الجمل. يمكننا اتباع نهج مماثل (أي نهج وضع العلامات التسلسلية) هنا أيضًا لاستخراج هذه الكيانات.
في مثال Dialogflow الخاصة بنا ، رأينا أنه يجب تحديد الفتحات و تعريفها مسبقًا. ولكن هنا ، نريد بناء هذا المكون بمفردنا باستخدام خوارزمية تعلم الألة. تذكر الخوارزميات التي تمت مناقشتها في سياق التعرف على الكيانات المسماة. سنستخدم خوارزميات مماثلة لاكتشاف الفتحات ووضع العلامات. سنستخدم مكتبة تصنيف تسلسل مفتوحة المصدر تسمى sklearn-crfsuite .
تتمثل إحدى عيوب هذه الأساليب في أنها تحتاج إلى الكثير من البيانات المصنفة لاكتشاف كل من الهدف والكيان. أيضًا ، نحتاج إلى نماذج مخصصة لكلتا المهمتين. هذا يمكن أن يجعل النظام بطيئا أثناء النشر. يعد الحصول على تسميات دقيقة للكيانات مكلفًا أيضًا. تحد هذه المشكلات من قابلية التوسع في خط الإنتاج لمزيد من المجالات.
كشفت الأبحاث الحديثة [هنا] حول فهم اللغة المنطوقة أن الفهم المشترك والتتبع أفضل من التصنيف الفردي وأجزاء تسمية التسلسل. هذا النموذج المشترك خفيف الوزن عند النشر مقارنة بالنماذج الفردية. للنمذجة المشتركة ، يمكننا استخدام حالات الحوار ، وهي “inform(price – cheap)” في مثالنا أعلاه.
يمكننا أن نهدف إلى ترتيب أو تسجيل كل زوج مرشح بالاشتراك مع نمط الحوار (عند الجمع ، حالة الحوار (dialog state) ) لتحديد الحالة بشكل مشترك. يعتبر التحديد المشترك أكثر تعقيدًا ويتطلب تقنيات تعلم تمثيلية أفضل الآن بعد أن ناقشنا مكونات نظام فهم معالجة اللغة الطبيعية (Natural Language Understanding ) ، دعنا ننتقل إلى إنشاء الاستجابة.
توليد الاستجابة
بمجرد تحديد الفتحات والهدف ، فإن الخطوة الأخيرة هي أن يقوم نظام الحوار بإنشاء استجابة مناسبة. هناك العديد من الطرق لإنشاء استجابة: الردود الثابتة ، واستخدام القوالب ، والتوليد التلقائي.
- ردود ثابتة
تستخدم روبوتات الأسئلة الشائعة بشكل أساسي الردود الثابتة. استنادًا إلى الغرض من الخانات الزمنية وقيمها ، يتم إجراء بحث في القاموس على مجموعة من الاستجابات واسترداد أفضل استجابة. تتمثل إحدى الحالات البسيطة في تجاهل معلومات الخانة والحصول على رد واحد لكل مقصد. للاسترداد الأكثر تعقيدًا ، يمكن إنشاء آلية ترتيب تصنف مجموعة الاستجابات بناءً على المقصد المكتشفة وأزواج قيمة الخانة (أو حالة الحوار).
- استخدام القوالب
لجعل الردود ديناميكية ، غالبًا ما يتم اتباع نهج قائم على القوالب. تكون القوالب مفيدة جدًا عندما تكون استجابة المتابعة سؤالًا توضيحيًا. يمكن استخدام قيم الفتحات للتوصل إلى سؤال متابعة أو إجابة مبنية على الحقائق. على سبيل المثال ، يمكن إنشاء “The House serves cheap Thai food” باستخدام قالب كما يلي :
<restaurant name> serves <price-value> <food-value> food
بمجرد تحديد الخانات وقيمها ، نقوم بتعبئة هذا القالب لتوليد استجابة مناسبة في النهاية.
- التوليد التلقائي
يمكن تعلم توليد لغة أكثر طبيعية و طلاقة باستخدام نهج قائم على البيانات. عند الحصول على حالة الحوار ، يمكن بناء نموذج توليدي شرطي يأخذ حالة الحوار كمدخل ويولد الاستجابة التالية للوكيل. يمكن أن تكون هذه النماذج نماذج رسومية أو نماذج لغة قائمة على التعلم العميق .
الآن بعد أن تعمقنا في المكونات المختلفة لنظام الحوار ، دعنا نتصفح أمثلة لتصنيف أنماط الحوار وتوقعات الخانات.
أمثلة الحوار
الآن ، سنستعرض أمثلة من مجموعات بيانات مختلفة من حوارات العالم الحقيقي المتاحة للجمهور ونناقش استخدامها لنمذجة جوانب مختلفة من نظام الحوار. ثم سنستخدم اثنتين من مجموعات البيانات هذه لإظهار كيفية تنفيذ نماذج لمهمتين وصفناهما لفهم السياق: توقع نمط الحوار أو تصنيف المقصد وتحديد الفتحة أو اكتشاف الكيان. سنستكشف نموذجين لكل مهمة ونبين من خلال المقارنات كيف يمكن تحسين هذه النماذج تدريجيًا.
مجموعات البيانات
هنا ملخص موجز لمجموعات البيانات المختلفة التي يتم استخدامها لقياس الخوارزميات لمهام الحوار الموجهة نحو الهدف. نظرًا لأننا مهتمون بمهام فهم معالجة اللغة الطبيعية (NLU) المختلفة في الحوارات ، فإننا نقدم أربع مجموعات بيانات لمربعات الحوار الموجهة نحو الهدف والتي تعمل كمعايير لمهام NLU القائمة على الحوار.
أولا: مجموعة بيانات تذاكر الطيران (ATIS )
معيار حجز تذاكر الطيران لتصنيف المقصد وملء الخانات الزمنية. هذه مجموعة بيانات ذات مجال واحد ، وبالتالي فإن الكيانات والأهداف مقصورة على مجال واحد.
ثانيا : مجموعة البيانات متعدد المجالات (SNIPS )
لتصنيف المقصد وملء الخانات . هذه مجموعة بيانات متعددة المجالات ، وبالتالي تنتمي الكيانات إلى مجالات متعددة. تشكل مجموعات البيانات متعددة المجالات تحديًا في وضع النماذج نظرًا لتنوعها.
ثالثا: مجموعة بيانات المطاعم (DSTC )
لتتبع حالة الحوار أو التحديد المشترك للمقاصد والخانات. هذا بالمثل مجموعة بيانات ذات نطاق واحد ، ولكن يتم التعبير عن الكيانات بشكل أكبر من حيث التعليقات التوضيحية وتحتوي على المزيد من البيانات الوصفية.
رابعاً : مجموعة البيانات متعدد المجالات (MultiWoZ )
لتتبع حالة الحوار أو التحديد المشترك للقصد و الخانات التي تمتد عبر مجالات متعددة. لسبب التباين المماثل ، تعد نمذجة مجموعة البيانات هذه أكثر صعوبة من نمذجة مجموعات ذات نطاق واحد.
على الرغم من وجود العديد من مجموعات البيانات مفتوحة المصدر ، إلا أنه لا يوجد سوى عدد قليل من مجموعات البيانات التي تعكس طبيعة المحادثات البشرية. تعاني مجموعات البيانات التي تم جمعها من قبل المعلقين عبر الإنترنت مثل Mechanical Turkers من محادثة فكرية وفرضية ، مما يؤثر على جودة الحوار. أيضًا ، لا تزال مجموعات بيانات الحوار الخاصة بالمجال غير متاحة للعديد من المجالات ، مثل الرعاية الصحية والقانون وما إلى ذلك.
توقع نمط الحوار
تصنيف نمط الحوار أو اكتشاف المقصد هو المهمة التي وصفناها في القسم السابق كجزء من مكون فهم معالجة اللغة NLU في نظام الحوار. هذه مهمة تصنيف ، وسوف نتبع خط إنتاج التصنيف الخاص بنا.
تحميل مجموعة البيانات
سنستخدم ATIS (أنظمة معلومات سفر شركات الطيران) لمهمة الكشف عن المقصد. ATIS هي مجموعة بيانات تُستخدم بكثرة لفهم اللغة المنطوقة وأداء مهام NLU المختلفة. تتكون مجموعة البيانات من 4،478 حوار تدريب و 893 حوار اختبار بإجمالي 21 مقصد. لقد اخترنا 17 مقصد، والتي تظهر في كل من التدريب ومجموعة الاختبار. ومن ثم ، فإن مهمتنا هي مهمة تصنيف 17 فئة. هنا مثال واحد من مجموعة البيانات:
Query text: BOS please list the flights from charlotte to long beach arriving after lunch time EOS
Intent label: flight
النموذج
شبكة لف رياضي عصبية (Convolutional neural network)
سنقوم في البداية بإستدعاء البيانات و تحهيزها ، و للقيام بذلك سنستعين بداله مساعده في ملف utils.py
import pandas as pd import numpy as np def get_data(filename): df = pd.read_csv(filename,delim_whitespace=True,names=['word','label']) beg_indices = list(df[df['word'] == 'BOS'].index)+[df.shape[0]] sents,labels,intents = [],[],[] for i in range(len(beg_indices[:-1])): sents.append(df[beg_indices[i]+1:beg_indices[i+1]-1]['word'].values) labels.append(df[beg_indices[i]+1:beg_indices[i+1]-1]['label'].values) intents.append(df.loc[beg_indices[i+1]-1]['label']) return np.array(sents, dtype=object), np.array(labels, dtype=object), np.array(intents, dtype=object) def get_data2(filename): with open(filename) as f: contents = f.read() sents,labels,intents = [],[],[] for line in contents.strip().split('\n'): words,labs = [i.split(' ') for i in line.split('\t')] sents.append(words[1:-1]) labels.append(labs[1:-1]) intents.append(labs[-1]) return np.array(sents, dtype=object), np.array(labels, dtype=object), np.array(intents, dtype=object) read_method = {'Data/data2/atis-2.dev.w-intent.iob':get_data, 'Data/data2/atis.train.w-intent.iob':get_data2, 'Data/data2/atis.test.w-intent.iob':get_data, 'Data/data2/atis-2.train.w-intent.iob':get_data2} def fetch_data(fname): func = read_method[fname] return func(fname)
بعد ذلك سنقوم بإستدعاء و تجهيز الملفات كالأتي
# إستيراد المكتبات import os import sys import numpy as np import random from numpy.core.numeric import indices import pandas as pd from tensorflow.keras.preprocessing.text import Tokenizer from tensorflow.keras.preprocessing.sequence import pad_sequences from tensorflow.keras.utils import to_categorical from tensorflow.keras.layers import Dense, Input, GlobalMaxPooling1D from tensorflow.keras.layers import Conv1D, MaxPooling1D, Embedding, LSTM from tensorflow.keras.models import Model, Sequential from tensorflow.keras.initializers import Constant from sklearn.preprocessing import LabelEncoder random.seed(0)#لإعادة إنتاج نفس النتائج ################################## ############# تحميل بيانات التدريب ######## # سيتم الإستعادنه بوظيفة مساعده from Data.utils import fetch_data, read_method sents, labels, intents = fetch_data('Data/data2/atis.train.w-intent.iob') train_sentense = [" ".join(i) for i in sents] train_texts = train_sentense train_label = intents.tolist() vals = [] for i in range (len(train_label)): if "#" in train_label[i] : vals.append(i) for i in vals[::-1]: train_label.pop(i) train_texts.pop(i) print ("Number of training sentences :",len(train_texts)) print ("Number of unique intents :",len(set(train_label))) for i in zip(train_texts[:5], train_label[:5]): print(i) ################################## ######## تحميل بيانات الإختبار ######## from Data.utils import fetch_data, read_method sents, labels, intents = fetch_data('Data/data2/atis.test.w-intent.iob') test_sentense = [" ".join(i) for i in sents] test_texts = test_sentense test_label = intents.tolist() new_label = set(test_label) - set(train_label) vals = [] for i in range (len(test_label)): if "#" in test_label[i] : vals.append(i) elif test_label[i] in new_label: print(test_label[i]) vals.append(i) for i in vals[::-1]: test_label.pop(i) test_texts.pop(i) print ("Number of test sentences :",len(test_texts)) print ("Number of unique intents :",len(set(test_label))) for i in zip(test_texts[:5], test_label[:5]): print(i) ################################## ######## المعالجة المسبقة ######## MAX_SEQUENCE_LENGTH = 300 MAX_NUM_WORDS = 20000 EMBEDDING_DIM = 100 VALIDATION_SPLIT = 0.3 tokenizer = Tokenizer(num_words=MAX_NUM_WORDS) tokenizer.fit_on_texts(train_texts) # تحويل النص إلى متجهة train_sequence = tokenizer.texts_to_sequences(train_texts) test_sequence = tokenizer.texts_to_sequences(test_texts) word_index = tokenizer.word_index print('Found %s unique tokens.' % len(word_index)) label_encoder = LabelEncoder() label_encoder.fit(train_label) train_label = label_encoder.transform(train_label) test_label = label_encoder.transform(test_label) # تحويل البيانات لتسلسل ليتم تغذيتها في نموذح الشبكة العصبية trainvalid_data = pad_sequences(train_sequence, maxlen=MAX_SEQUENCE_LENGTH) test_data = pad_sequences(test_sequence,maxlen=MAX_SEQUENCE_LENGTH) trainvalid_label = to_categorical(train_label) test_label = to_categorical(np.asarray(test_label), num_classes=trainvalid_label.shape[1]) # تقسيم بيانات التدريب إلى تدريب و تحقق # هذه البيانات التي سيتم إستخدامها لتدريب الشبكة العصبية indices = np.arange(trainvalid_data.shape[0]) np.random.shuffle(indices) trainvalid_data = trainvalid_data[indices] trainvalid_label = trainvalid_label[indices] num_validation_sample = int(VALIDATION_SPLIT * trainvalid_data.shape[0]) x_train = trainvalid_data[:-num_validation_sample] y_train = trainvalid_label[:-num_validation_sample] x_val = trainvalid_data[-num_validation_sample:] y_val = trainvalid_label[-num_validation_sample:] print('Splitting the train data into train and valid is done') # بناء مصفوفة التضمين print('Preparing embedding matrix.') # أولا نقوم بربط الكلمات في مجموعة التضمين إلى متجهات التضمين #Glove 6B سنستخدم BASE_DIR = 'Data' GLOVE_DIR = os.path.join(BASE_DIR, 'glove.6B') embedding_index = {} with open(os.path.join(GLOVE_DIR, 'glove.6B.100d.txt'), encoding="utf-8") as f: for line in f: values = line.split() word = values[0] coef = np.asarray(values[1:], dtype='float32') embedding_index[word] = coef print('Found %s word vectors in Glove embeddings.' % len(embedding_index)) # تجهيز مصفوفة التضمين # word_index الصفوف هي الكلمات من # glove أما الأعمده فهي التضمينات من num_words = min(MAX_NUM_WORDS, len(word_index)) + 1 embedding_matrix = np.zeros((num_words, EMBEDDING_DIM)) for word , i in word_index.items() : if i > MAX_NUM_WORDS: continue embedding_vector = embedding_index.get(word) if embedding_vector is not None: embedding_matrix[i] = embedding_vector # تحميل التضمينات المدربة في طبقة التضمين embedding_layer = Embedding(num_words, EMBEDDING_DIM, embeddings_initializer=Constant(embedding_matrix), input_length=MAX_SEQUENCE_LENGTH, trainable=False) print("Preparing of embedding matrix is done")
نظرًا لأنها مهمة تصنيف ، سنستخدم إحدى تقنيات تعلم الألة التي استخدمناها في هذا المقال : نموذج شبكة لف رياضي عصبية (CNN ). يعد استخدام CNN مفيدًا هنا لأنه يلتقط سمات n-gram عبر تمثيلاتها الكثيفة. تشير N-grams مثل “قائمة الرحلات الجوية” إلى تسمية “رحلة”:
cnnmodel = Sequential() cnnmodel.add(Embedding(MAX_NUM_WORDS, 128)) cnnmodel.add(Conv1D(128, 5, activation='relu')) cnnmodel.add(MaxPooling1D(5)) cnnmodel.add(Conv1D(128, 5, activation='relu')) cnnmodel.add(MaxPooling1D(5)) cnnmodel.add(Conv1D(128, 5, activation='relu')) cnnmodel.add(GlobalMaxPooling1D()) cnnmodel.add(Dense(128, activation='relu')) cnnmodel.add(Dense(len(trainvalid_label[0]), activation='softmax')) cnnmodel.compile(loss='categorical_crossentropy', optimizer='rmsprop', metrics=['acc']) cnnmodel.summary() # تدريب النموذج cnnmodel.fit(x_train, y_train, batch_size=128, epochs=1, validation_data=(x_val, y_val)) # التحقق على بيانات الإختبار score, acc = cnnmodel.evaluate(test_data, test_label) print('Test accuracy with CNN:', acc)
نحصل على دقة تصل إلى 72٪ باستخدام شبكة CNN في الاختبار ، بمتوسط جميع الفئات.
شبكة عصبية متكررةِ (Recurrent neural network)
إذا استخدمنا نموذج شبكة عصبية متكررة RNN ، فإن الدقة تصل إلى 96٪. نعتقد أن RNN قادرة على التقاط ترابط الكلمات عبر جملة الإدخال. تلتقط RNN أهمية الكلمة فيما يتعلق بالسياق الذي تمت رؤيته من قبل.
rnnmodel2 = Sequential() rnnmodel2.add(embedding_layer) rnnmodel2.add(LSTM(128, dropout=0.2, recurrent_dropout=0.2)) rnnmodel2.add(Dense(len(trainvalid_label[0]), activation='sigmoid')) rnnmodel2.compile(loss='binary_crossentropy', optimizer='adam', metrics=['accuracy']) rnnmodel2.summary() print('Training the RNN') rnnmodel2.fit(x_train, y_train, batch_size=32, epochs=1, validation_data=(x_val, y_val)) score, acc = rnnmodel2.evaluate(test_data, test_label, batch_size=32) print('Test accuracy with RNN:', acc)
نماذج المحولات
كما نعلم ، فإن نماذج المحولات الحديثة المدربة مسبقًا (مثل BERT) أكثر قوة. لذلك ، سنحاول استخدام BERT لتحسين الأداء الذي تم الحصول عليه حتى الآن. يمكن أن يلتقط BERT السياق بشكل أفضل ويحتوي على المزيد من المعايير، لذا فهو أكثر تعبيرًا وينمذجة تعقيدات اللغة. لاستخدام BERT ، نستخدم مخطط تعميل بنمط BERT:
# إستدعاء المكتبات import pickle import io import pandas as pd import numpy as np from tqdm import tqdm, trange from sklearn.model_selection import train_test_split import matplotlib.pyplot as plt from seqeval.metrics import f1_score from sklearn.preprocessing import LabelEncoder import torch from torch.utils.data import TensorDataset, DataLoader, RandomSampler, SequentialSampler from torch.optim import Adam from pytorch_pretrained_bert import BertTokenizer, BertConfig from pytorch_pretrained_bert import BertAdam, BertForSequenceClassification import tensorflow as tf from tensorflow.keras.preprocessing.sequence import pad_sequences # إستخدام معالج الرسومات device = torch.device("cuda" if torch.cuda.is_available() else "cpu") n_gpu = torch.cuda.device_count() torch.cuda.get_device_name(0) # البيانات DATA_DIR="." ################################### #########وظائف المعالجة المسبقة####### def get_data(filename): df = pd.read_csv(filename,delim_whitespace=True,names=['word','label']) beg_indices = list(df[df['word'] == 'BOS'].index)+[df.shape[0]] sents,labels,intents = [],[],[] for i in range(len(beg_indices[:-1])): sents.append(df[beg_indices[i]+1:beg_indices[i+1]-1]['word'].values) labels.append(df[beg_indices[i]+1:beg_indices[i+1]-1]['label'].values) intents.append(df.loc[beg_indices[i+1]-1]['label']) return np.array(sents),np.array(labels),np.array(intents) def get_data2(filename): with open(filename) as f: contents = f.read() sents,labels,intents = [],[],[] for line in contents.strip().split('\n'): words,labs = [i.split(' ') for i in line.split('\t')] sents.append(words[1:-1]) labels.append(labs[1:-1]) intents.append(labs[-1]) return np.array(sents),np.array(labels),np.array(intents) read_method = {'atis-2.dev.w-intent.iob':get_data, 'atis.train.w-intent.iob':get_data2, 'atis.test.w-intent.iob':get_data, 'atis-2.train.w-intent.iob':get_data2} def fetch_data(fname): func = read_method[fname] return func(fname) ### قراءة البيانات print("Loading the training Data") sents,labels,intents = fetch_data('atis.train.w-intent.iob') train_sentences = [" ".join(i) for i in sents] train_texts = train_sentences train_labels= intents.tolist() vals = [] for i in range(len(train_labels)): if "#" in train_labels[i]: vals.append(i) for i in vals[::-1]: train_labels.pop(i) train_texts.pop(i) print ("Number of training sentences :",len(train_texts)) print ("Number of unique intents :",len(set(train_labels))) for i in zip(train_texts[:5], train_labels[:5]): print(i) # تحويل البيانات إلى باندا from sklearn.preprocessing import LabelEncoder # df = pd.DataFrame(data =zip(train_labels,train_texts),columns=['Labels',"Text"]) lb_make = LabelEncoder() df["Labels"] = lb_make.fit_transform(df["Labels"]) train_texts = list(df['Text']) train_labels = list(df['Labels']) print("Length of training labels:",len(train_labels)) print("Length of training texts:",len(train_texts)) # تحويل البيانات و حعلها في صيغة بيرت query_data_train = list(train_texts) sentences = ["[CLS] " + query + " [SEP]" for query in query_data_train] print(sentences[0]) tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True) tokenized_texts = [tokenizer.tokenize(sent) for sent in sentences] print ("Tokenize the first sentence:") print (tokenized_texts[0]) # تحديد أقصى طول MAX_LEN = 128 # حشو العملات المدخلة input_ids = pad_sequences([tokenizer.convert_tokens_to_ids(txt) for txt in tokenized_texts], maxlen=MAX_LEN, dtype="long", truncating="post", padding="post") # نستعمل تعميل بيرت لتحويل العملات إلى أرقام الفهرس في معجم بيرت input_ids = [tokenizer.convert_tokens_to_ids(x) for x in tokenized_texts] input_ids = pad_sequences(input_ids, maxlen=MAX_LEN, dtype="long", truncating="post", padding="post") # إنشاء أقنعة الإنتباه attention_masks = [] for seq in input_ids: seq_mask = [float(i>0) for i in seq] attention_masks.append(seq_mask) intent_data_label_train = train_labels # تقسيم البيانات لتدريب و إختبار train_inputs, validation_inputs, train_labels, validation_labels = train_test_split(input_ids, intent_data_label_train, random_state=2020, test_size=0.1) train_masks, validation_masks, _, _ = train_test_split(attention_masks, input_ids, random_state=2020, test_size=0.1) # Convert all of our data into torch tensors, the required datatype for our model # تحويل جميع بياناتنا إلى تنسورات بايتورش train_inputs = torch.tensor(train_inputs) validation_inputs = torch.tensor(validation_inputs) train_labels = torch.tensor(train_labels) validation_labels = torch.tensor(validation_labels) train_masks = torch.tensor(train_masks) validation_masks = torch.tensor(validation_masks) batch_size = 32 train_data = TensorDataset(train_inputs, train_masks, train_labels) train_sampler = RandomSampler(train_data) train_dataloader = DataLoader(train_data, sampler=train_sampler, batch_size=batch_size) validation_data = TensorDataset(validation_inputs, validation_masks, validation_labels) validation_sampler = SequentialSampler(validation_data) validation_dataloader = DataLoader(validation_data, sampler=validation_sampler, batch_size=batch_size) #################################### #################### بناء بيرت ####### model = BertForSequenceClassification.from_pretrained("bert-base-uncased", num_labels=17) model.cuda() # تحسين معايير بيرت FULL_FINETUNING = True if FULL_FINETUNING: param_optimizer = list(model.named_parameters()) no_decay = ['bias', 'gamma', 'beta'] optimizer_grouped_parameters = [ {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay_rate': 0.01}, {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay_rate': 0.0} ] else: param_optimizer = list(model.classifier.named_parameters()) optimizer_grouped_parameters = [{"params": [p for n, p in param_optimizer]}] optimizer = BertAdam(optimizer_grouped_parameters, lr=3e-5) # وظيفة لحساب الدقة def flat_accuracy(preds, labels): pred_flat = np.argmax(preds, axis=1).flatten() labels_flat = labels.flatten() return np.sum(pred_flat == labels_flat) / len(labels_flat) train_loss_set = [] epochs = 4 # حلقة تدريب بيرت for _ in trange(epochs, desc="Epoch"): ###### التدريب ###### model.train() tr_loss = 0 nb_tr_examples, nb_tr_steps = 0, 0 # تدريب البيانات لحقبة واحدة for step, batch in enumerate(train_dataloader): batch = tuple(t.to(device) for t in batch) b_input_ids, b_input_mask, b_labels = batch optimizer.zero_grad() # التغذية الأمامية loss = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask, labels=b_labels) train_loss_set.append(loss.item()) # التغذية الرجعية loss.backward() optimizer.step() tr_loss += loss.item() nb_tr_examples += b_input_ids.size(0) nb_tr_steps += 1 print("Train loss: {}".format(tr_loss/nb_tr_steps)) ###### التحقق ###### model.eval() eval_loss, eval_accuracy = 0, 0 nb_eval_steps, nb_eval_examples = 0, 0 # تجقق من البيانات لحقبة واحدة for batch in validation_dataloader: batch = tuple(t.to(device) for t in batch) b_input_ids, b_input_mask, b_labels = batch # إخبار النموذج بأن لا يحسب أو يحفظ المشتقات with torch.no_grad(): logits = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask) logits = logits.detach().cpu().numpy() label_ids = b_labels.to('cpu').numpy() tmp_eval_accuracy = flat_accuracy(logits, label_ids) eval_accuracy += tmp_eval_accuracy nb_eval_steps += 1 print("Validation Accuracy: {}".format(eval_accuracy/nb_eval_steps)) # طباعة أداء النموذح plt.figure(figsize=(15,8)) plt.title("Training loss") plt.xlabel("Batch") plt.ylabel("Loss") plt.plot(train_loss_set) plt.show()
نظرًا لأن BERT مدرب مسبقًا ، فإن تمثيل المحتوى أفضل بكثير من أي نماذج ندربها من البداية ، مثل CNNs أو RNNs. نرى أن BERT يحقق دقة بنسبة 98.8٪ ، متغلبًا على كل من CNN و RNN في مهمة التنبؤ بفعل الحوار.
تحديد الخانة
يعد تحديد الخانة مهمة أخرى وصفناها كجزء من مكون فهم اللغة الطبيعية (NLU) في نظام الحوار. وصفنا لماذا يمكننا أن نضع هذا كمهمة تصنيف تسلسل. نحتاج إلى إيجاد قيم الخانة مع الأخذ في الاعتبار الإدخال.
تحميل مجموعة البيانات
سنستخدم SNIPS لمهمة تحديد الخانة هذه. يحتوي على 16000 استعلام جماعي وهو معيار شائع لمهام تحديد الخانات. سنحمل كل من أمثلة التدريب والاختبار ، وهنا مثال واحد لمجموعة البيانات على النحو التالي:
Query text: [Play, Magic, Sam, from, the, thirties] # tokenized
Slots: [O, artist-1, artist-2, O, O, year-1]
كما ناقشنا في المقال 14 من سلسلة مقدمة في معالجة اللغة الطبيعية نحن نستخدم مخطط BIO لتعليق الخانات. هنا ، يشير O إلى “آخر” ، ويشير اartist-1 وartist-2 إلى كلمتين لاسم الفنان. الشيء نفسه ينطبق على العام.
النموذج
نموذج CRF ++
نظرًا لأنه يمكن النظر إلى مهمة تحديد الخانة على أنها مهمة تصنيف تسلسل ، فسنستخدم إحدى الأساليب الشائعة التي استخدمناها في المقال 14 : نموذج CRF ++ من حزمة sklearn. نستخدم أيضًا متجهات الكلمات بدلاً من إنشاء سمات مصنوعة يدويًا لتغذية نموذج CRF. CRFs هي تقنية شائعة لوضع العلامات التسلسلية وتستخدم بكثرة في استخراج المعلومات.
نحن نستخدم سمات الكلمات التي ستكون مفيدة لهذه المهمة بالذات. نرى أن سياق كل كلمة مهم بالإضافة إلى معنى الكلمة نفسها. لذلك ، نستخدم الكلمتين السابقتين والكلمتين التاليتين لكلمة معينة كسمات . نستخدم أيضًا متجهات التضمين المسترجعة من التضمينات المدربة مسبقًا من GloVe كسمات إضافية. يتم ربط سمات كل كلمة عبر الكلمات في الإدخال. يتم تمرير تمثيل الإدخال هذا إلى نموذج CRF لتسمية التسلسل:
# إستدعاء البيانات import os import json import string import numpy as np from nltk.tag import pos_tag from sklearn_crfsuite import CRF, metrics from sklearn.metrics import make_scorer,confusion_matrix from sklearn.metrics import f1_score,classification_report from sklearn.pipeline import Pipeline from pprint import pprint from tensorflow.keras.preprocessing.text import Tokenizer # تجهيز البيانات train_loc = "Data/snips/train_PlayMusic_full.json" test_loc = "Data/snips/validate_PlayMusic.json" train_file = json.load(open(train_loc, encoding= "iso-8859-2")) test_file = json.load(open(test_loc, encoding= "iso-8859-2")) train_datafile = [i["data"] for i in train_file["PlayMusic"]] test_datafile = [i["data"] for i in test_file["PlayMusic"]] def convert_data(datalist): output = [] for data in datalist: sent = [] pos = [] for phrase in data: words = phrase["text"].strip().split(" ") while "" in words: words.remove("") if "entity" in phrase.keys(): label = phrase["entity"] labels = [label+"-{}".format(i+1) for i in range(len(words))] else: labels = ["O"] * len(words) sent.extend(words) pos.extend(labels) output.append([sent, pos]) print(sent) return output train_data = convert_data(train_datafile) test_data = convert_data(test_datafile) BASE_DIR = 'Data' GLOVE_DIR = os.path.join(BASE_DIR, 'glove.6B') MAX_SEQUENCE_LENGTH = 300 MAX_NUM_WORDS = 20000 EMBEDDING_DIM = 100 VALIDATION_SPLIT = 0.3 # تجهيز مصفوفة التضمينات embeddings_index = {} with open(os.path.join(GLOVE_DIR, 'glove.6B.100d.txt'), encoding="utf-8") as f: for line in f: values = line.split() word = values[0] coefs = np.asarray(values[1:], dtype='float32') embeddings_index[word] = coefs print('Found %s word vectors in Glove embeddings.' % len(embeddings_index)) def get_embeddings(word): embedding_vector = embeddings_index.get(word) if embedding_vector is None: # words not found in embedding index will be all-zeros. embedding_vector = np.zeros(shape=(EMBEDDING_DIM, )) return embedding_vector train_texts = [" ".join(i[0]) for i in train_data] test_texts = [" ".join(i[0]) for i in test_data] train_texts[0] tokenizer = Tokenizer(num_words=MAX_NUM_WORDS) tokenizer.fit_on_texts(train_texts) train_sequences = tokenizer.texts_to_sequences(train_texts) test_sequences = tokenizer.texts_to_sequences(test_texts) word_index = tokenizer.word_index print('Found %s unique tokens.' % len(word_index)) # الحصول على السمات من الجملة def sent2feats(sentence): feats = [] sen_tags = pos_tag(sentence) for i in range(0,len(sentence)): word = sentence[i] wordfeats = {} wordfeats['word'] = word if i == 0: wordfeats["prevWord"] = wordfeats["prevSecondWord"] = "<S>" elif i==1: wordfeats["prevWord"] = sentence[0] wordfeats["prevSecondWord"] = "</S>" else: wordfeats["prevWord"] = sentence[i-1] wordfeats["prevSecondWord"] = sentence[i-2] if i == len(sentence)-2: wordfeats["nextWord"] = sentence[i+1] wordfeats["nextNextWord"] = "</S>" elif i==len(sentence)-1: wordfeats["nextWord"] = "</S>" wordfeats["nextNextWord"] = "</S>" else: wordfeats["nextWord"] = sentence[i+1] wordfeats["nextNextWord"] = sentence[i+2] wordfeats['tag'] = sen_tags[i][1] if i == 0: wordfeats["prevTag"] = wordfeats["prevSecondTag"] = "<S>" elif i == 1: wordfeats["prevTag"] = sen_tags[0][1] wordfeats["prevSecondTag"] = "</S>" else: wordfeats["prevTag"] = sen_tags[i - 1][1] wordfeats["prevSecondTag"] = sen_tags[i - 2][1] if i == len(sentence) - 2: wordfeats["nextTag"] = sen_tags[i + 1][1] wordfeats["nextNextTag"] = "</S>" elif i == len(sentence) - 1: wordfeats["nextTag"] = "</S>" wordfeats["nextNextTag"] = "</S>" else: wordfeats["nextTag"] = sen_tags[i + 1][1] wordfeats["nextNextTag"] = sen_tags[i + 2][1] vector = get_embeddings(word) for iv,value in enumerate(vector): wordfeats['v{}'.format(iv)]=value feats.append(wordfeats) return feats # Conll إستخراج السمات من بيانات def get_feats_conll(conll_data): feats = [] labels = [] for sentence in conll_data: feats.append(sent2feats(sentence[0])) labels.append(sentence[1]) return feats, labels ### تدريب النموذج def train_seq(X_train,Y_train,X_dev,Y_dev): crf = CRF(algorithm='lbfgs', c1=0.1, c2=10, max_iterations=50) crf.fit(X_train, Y_train) labels = list(crf.classes_) y_pred = crf.predict(X_dev) sorted_labels = sorted(labels, key=lambda name: (name[1:], name[0])) print(metrics.flat_f1_score(Y_dev, y_pred,average='weighted', labels=labels)) print(metrics.flat_classification_report(Y_dev, y_pred, labels=sorted_labels, digits=3)) get_confusion_matrix(Y_dev, y_pred,labels=sorted_labels) # الحصول على مصفوفة الإرتباك def print_cm(cm, labels): print("\n") """pretty print for confusion matrixes""" columnwidth = max([len(x) for x in labels] + [5]) # 5 is value length empty_cell = " " * columnwidth # Print header print(" " + empty_cell, end=" ") for label in labels: print("%{0}s".format(columnwidth) % label, end=" ") print() # Print rows for i, label1 in enumerate(labels): print(" %{0}s".format(columnwidth) % label1, end=" ") sum = 0 for j in range(len(labels)): cell = "%{0}.0f".format(columnwidth) % cm[i, j] sum = sum + int(cell) print(cell, end=" ") print(sum ) def get_confusion_matrix(y_true,y_pred,labels): trues,preds = [], [] for yseq_true, yseq_pred in zip(y_true, y_pred): trues.extend(yseq_true) preds.extend(yseq_pred) print_cm(confusion_matrix(trues,preds,labels),labels) # CRF تدريب النموذج مع print("Training a Sequence classification model with CRF") feats, labels = get_feats_conll(train_data) devfeats, devlabels = get_feats_conll(test_data) train_seq(feats, labels, devfeats, devlabels) print("Done with sequence model")
نحصل على نتيجة F1 تساوي 85.5 باستخدام نموذج CRF ++.
نموذج Bert
على غرار مهمة التصنيف السابقة ، سنحاول استخدام BERT لتحسين الأداء الذي تم الحصول عليه حتى الآن. يمكن لـ BERT التقاط السياق بشكل أفضل ، حتى في حالة مهمة تسمية التسلسل (sequence labeling). نستخدم جميع التمثيلات المخفية لجميع الكلمات في الاستعلام للتنبؤ بتسمية لكل منها. ومن ثم ، في النهاية ، نقوم بإدخال تسلسل الكلمات في النموذج والحصول على سلسلة من التسميات (بنفس طول المدخلات) ، والتي يمكن استنتاجها على أنها خانات متوقعة بالكلمات كقيم:
# إستدعاء المكتبات import string import re import json import pandas as pd import numpy as np from tqdm import tqdm, trange import torch from torch.optim import Adam from torch.utils.data import TensorDataset, DataLoader, RandomSampler, SequentialSampler from pytorch_pretrained_bert import BertTokenizer, BertConfig from pytorch_pretrained_bert import BertForTokenClassification, BertAdam from tensorflow.keras.preprocessing.sequence import pad_sequences from sklearn.model_selection import train_test_split from seqeval.metrics import f1_score device = torch.device("cuda" if torch.cuda.is_available() else "cpu") n_gpu = torch.cuda.device_count() # تحهيز البيانات train_loc = "train_PlayMusic_full.json" test_loc = "validate_PlayMusic.json" train_file = json.load(open(train_loc, encoding= "iso-8859-2")) test_file = json.load(open(test_loc, encoding= "iso-8859-2")) train_datafile = [i["data"] for i in train_file["PlayMusic"]] test_datafile = [i["data"] for i in test_file["PlayMusic"]] # وظيفة مساعدة لمعالجة البيانات def convert_data(datalist): output = [] for data in datalist: sent = [] pos = [] for phrase in data: words = phrase["text"].strip().split(" ") while "" in words: words.remove("") if "entity" in phrase.keys(): label = phrase["entity"] labels = [label+"-{}".format(i+1) for i in range(len(words))] else: labels = ["O"] * len(words) sent.extend(words) pos.extend(labels) output.append([sent, pos]) # print(sent) return output train_data = convert_data(train_datafile) test_data = convert_data(test_datafile) df_train = pd.DataFrame(train_data,columns=['sentence','label']) df_test = pd.DataFrame(test_data,columns=['sentence','label']) sentence = list(df_train['sentence'])+list(df_test['sentence']) label = list(df_train['label'])+list(df_test['label']) unique_labels =[] for i in label: unique_labels += i labels = unique_labels unique_labels = set(unique_labels) # نختاج إلى دمج العملات في جملة واحده import re def untokenize(words): """ Untokenizing a text undoes the tokenizing operation, restoring punctuation and spaces to the places that people expect them to be. Ideally, `untokenize(tokenize(text))` should be identical to `text`, except for line breaks. """ text = ' '.join(words) step1 = text.replace("`` ", '"').replace(" ''", '"').replace('. . .', '...') step2 = step1.replace(" ( ", " (").replace(" ) ", ") ") step3 = re.sub(r' ([.,:;?!%]+)([ \'"`])', r"\1\2", step2) step4 = re.sub(r' ([.,:;?!%]+)$', r"\1", step3) step5 = step4.replace(" '", "'").replace(" n't", "n't").replace( "can not", "cannot") step6 = step5.replace(" ` ", " '") return step6.strip() sentences_untokenized = [untokenize(i) for i in sentence] # تجهيز نموذج بيرت MAX_LEN = 75 bs = 32 tokenizer = BertTokenizer.from_pretrained('bert-base-uncased', do_lower_case=True) tokenized_texts = [["[CLS]"] +tokenizer.tokenize(sent)+ ["[SEP]"] for sent in sentences_untokenized] # المعالجة المسبقة للتسميات tags_vals = list(unique_labels) tag2idx = {t: i for i, t in enumerate(tags_vals)} tags_vals[:10] # قص و حشو العملات input_ids = pad_sequences([tokenizer.convert_tokens_to_ids(txt) for txt in tokenized_texts], maxlen=MAX_LEN, dtype="long", truncating="post", padding="post") tags = pad_sequences([[tag2idx.get(l) for l in lab] for lab in label], maxlen=MAX_LEN, value=tag2idx["O"], padding="post", dtype="long", truncating="post") # تجهيز أقنعة الإنتباه attention_masks = [[float(i>0) for i in ii] for ii in input_ids] # تقسيم البيانات tr_inputs, val_inputs, tr_tags, val_tags = train_test_split(input_ids, tags, random_state=2018, test_size=0.1) tr_masks, val_masks, _, _ = train_test_split(attention_masks, input_ids, random_state=2018, test_size=0.1) # تحويل المدخلات إلى تنسورات بايتورش tr_inputs = torch.tensor(tr_inputs) val_inputs = torch.tensor(val_inputs) tr_tags = torch.tensor(tr_tags) val_tags = torch.tensor(val_tags) tr_masks = torch.tensor(tr_masks) val_masks = torch.tensor(val_masks) train_data = TensorDataset(tr_inputs, tr_masks, tr_tags) train_sampler = RandomSampler(train_data) train_dataloader = DataLoader(train_data, sampler=train_sampler, batch_size=bs) valid_data = TensorDataset(val_inputs, val_masks, val_tags) valid_sampler = SequentialSampler(valid_data) valid_dataloader = DataLoader(valid_data, sampler=valid_sampler, batch_size=bs) # ضبط بيرت model = BertForTokenClassification.from_pretrained("bert-base-uncased", num_labels=len(tag2idx)) model.cuda() FULL_FINETUNING = True if FULL_FINETUNING: param_optimizer = list(model.named_parameters()) no_decay = ['bias', 'gamma', 'beta'] optimizer_grouped_parameters = [ {'params': [p for n, p in param_optimizer if not any(nd in n for nd in no_decay)], 'weight_decay_rate': 0.01}, {'params': [p for n, p in param_optimizer if any(nd in n for nd in no_decay)], 'weight_decay_rate': 0.0} ] else: param_optimizer = list(model.classifier.named_parameters()) optimizer_grouped_parameters = [{"params": [p for n, p in param_optimizer]}] optimizer = Adam(optimizer_grouped_parameters, lr=3e-5) # الدقة def flat_accuracy(preds, labels): pred_flat = np.argmax(preds, axis=2).flatten() labels_flat = labels.flatten() return np.sum(pred_flat == labels_flat) / len(labels_flat) # التدريب epochs = 5 max_grad_norm = 1.0 device = torch.device("cuda" if torch.cuda.is_available() else "cpu") n_gpu = torch.cuda.device_count() train_loss_set = [] for _ in trange(epochs, desc="Epoch"): model.train() tr_loss = 0 nb_tr_examples, nb_tr_steps = 0, 0 for step, batch in enumerate(train_dataloader): batch = tuple(t.to(device) for t in batch) b_input_ids, b_input_mask, b_labels = batch # التغذية الأمامية loss = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask, labels=b_labels) train_loss_set.append(loss.item()) # التغذية الرجعية loss.backward() tr_loss += loss.item() nb_tr_examples += b_input_ids.size(0) nb_tr_steps += 1 # قص المشتقات torch.nn.utils.clip_grad_norm_(parameters=model.parameters(), max_norm=max_grad_norm) optimizer.step() model.zero_grad() print("Train loss: {}".format(tr_loss/nb_tr_steps)) model.eval() eval_loss, eval_accuracy = 0, 0 nb_eval_steps, nb_eval_examples = 0, 0 predictions , true_labels = [], [] for batch in valid_dataloader: batch = tuple(t.to(device) for t in batch) b_input_ids, b_input_mask, b_labels = batch with torch.no_grad(): tmp_eval_loss = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask, labels=b_labels) logits = model(b_input_ids, token_type_ids=None, attention_mask=b_input_mask) logits = logits.detach().cpu().numpy() label_ids = b_labels.to('cpu').numpy() predictions.extend([list(p) for p in np.argmax(logits, axis=2)]) true_labels.append(label_ids) tmp_eval_accuracy = flat_accuracy(logits, label_ids) eval_loss += tmp_eval_loss.mean().item() eval_accuracy += tmp_eval_accuracy nb_eval_examples += b_input_ids.size(0) nb_eval_steps += 1 eval_loss = eval_loss/nb_eval_steps print("Validation loss: {}".format(eval_loss)) print("Validation Accuracy: {}".format(eval_accuracy/nb_eval_steps)) pred_tags = [tags_vals[p_i] for p in predictions for p_i in p] valid_tags = [tags_vals[l_ii] for l in true_labels for l_i in l for l_ii in l_i] print("F1-Score: {}".format(f1_score(pred_tags, valid_tags)))
لكننا وجدنا أن BERT يحقق 73= F1 فقط. قد يكون هذا بسبب وجود العديد من الكيانات المسماة في المدخلات التي لم يتم تمثيلها بشكل جيد بواسطة معايير BERT الأصلية. من ناحية أخرى ، كانت السمات التي حصلنا عليها لـ CRF قوية بما يكفي لمجموعة البيانات هذه لالتقاط الأنماط اللازمة. هذا مثال مثير للاهتمام حيث يتفوق نموذج أبسط على BERT.
ختاما
لقد تعلمنا حتى الآن كيفية إنشاء مكونات مختلفة لـ NLU من أجل حوار موجه نحو الهدف باستخدام مجموعات البيانات الشائعة. لقد رأينا كيف تعمل نماذج تعلم الألة المختلفة بشكل جيد نسبيًا في هذه المهام. باستخدام هذه ، سنتمكن من تشغيل مثل هذه النماذج المخصصة في مجموعة البيانات الخاصة بنا واستكشاف نماذج مختلفة لاختيار أفضلها.
كما رأينا من قبل وهنا أيضًا ، تساعد النماذج المدربة مسبقًا في الحصول على أداء أفضل مقارنة بنماذج DL الأخرى التي تم تعلمها من البداية. قد تكون هناك استثناءات ، لأن النماذج المدربة مسبقًا حساسة لحجم البيانات. قد تزيد النماذج المدربة مسبقًا على مجموعات البيانات الأصغر ، وقد يتم تعميم الميزات المصنوعة يدويًا بشكل جيد في هذه الحالات.
إضافة تعليق