دخول
تعتبر معالجة اللغات الطبيعية (NLP) مجالًا مهمًا وشائعًا للتعلم الآلي. في هذا المحور ، سوف أصف أول مشروع لي يتعلق بتحليل التلوين العاطفي لمراجعات الأفلام المكتوبة في بيثون. مهمة التحليل العاطفي شائعة جدًا بين أولئك الذين يرغبون في إتقان المفاهيم الأساسية لبرنامج البرمجة اللغوية العصبية ، ويمكن أن يصبحوا نظيرًا لـ "عالم الترحيب" في هذا المجال.
في هذه المقالة ، سنستعرض جميع المراحل الرئيسية لعملية علم البيانات: من إنشاء مجموعة البيانات الخاصة بك ومعالجتها واستخراج الميزات باستخدام مكتبة NLTK ، وأخيراً تدريب النموذج وضبطه باستخدام scikit-Learn. المهمة نفسها هي تصنيف المراجعات إلى ثلاث فئات: سلبية ، محايدة وإيجابية.
تشكيل مجموعة البيانات
لحل هذه المشكلة ، يمكن للمرء استخدام بعض البيانات الجاهزة والمشروح بها مع تعليقات من IMDB ، والتي يوجد الكثير منها على GitHub. ولكن تقرر إنشاء الخاصة بك مع ملاحظات باللغة الروسية مأخوذة من Kinopoisk. من أجل عدم نسخها يدويًا ، سنكتب محلل ويب.
سأستخدم مكتبة
الطلبات لإرسال
طلبات http ، و
BeautifulSoup لمعالجة ملفات html. أولاً ، دعنا نحدد وظيفة ستأخذ رابطًا لمراجعات الأفلام واستعادتها. لكي لا يتعرف Kinopoisk على البوت الموجود فينا ، يجب عليك تحديد وسيطة
الرؤوس في الدالة applications.get ، والتي ستحاكي المتصفح. من الضروري تمرير قاموس فيه باستخدام مفاتيح User-Agent و Accept-language و Accept ، حيث يمكن العثور على قيمها في أدوات مطور المتصفح. بعد ذلك ، يتم إنشاء محلل وتخزين المراجعات من الصفحة ، والتي يتم تخزينها في فئة ترميز HTML _reachbanner_.
import requests from bs4 import BeautifulSoup import numpy as np import time import os def load_data(url): r = requests.get(url, headers = headers)
لقد تخلصنا من علامة html ، ومع ذلك ، لا تزال
مراجعاتنا عبارة عن كائنات
BeautifulSoup ، لكننا بحاجة إلى تحويلها إلى سلاسل. وظيفة
تحويل يفعل ذلك تماما. سنقوم أيضًا بكتابة وظيفة تسترجع اسم الفيلم ، والتي سيتم استخدامها لاحقًا لحفظ المراجعات.
def convert(reviews):
سوف تأخذ الوظيفة الأخيرة للمحلل رابطًا إلى الصفحة الرئيسية من الفيلم ، وفئة مراجعة وطريقة لحفظ المراجعات. تحدد الوظيفة أيضًا
التأخيرات بين الطلبات الضرورية لتجنب الحظر. تحتوي الوظيفة على حلقة تقوم باسترداد وتخزين المراجعات بدءًا من الصفحة الأولى ، حتى تصادف صفحة غير موجودة
تستخرج منها وظيفة
load_data قائمة فارغة
وستتوقف الحلقة.
def parsing(url, status, path): page = 1 delays = [11, 12, 13, 11.5, 12.5, 13.5, 11.2, 12.3, 11.8] name = get_name(url) time.sleep(np.random.choice(delays))
ثم ، باستخدام الدورة التالية ، يمكنك استخراج المراجعات من الأفلام الموجودة في قائمة
urles . سوف تحتاج إلى إنشاء قائمة الأفلام يدويًا. سيكون من الممكن ، على سبيل المثال ، الحصول على قائمة بروابط للأفلام عن طريق كتابة وظيفة من شأنها أن تستخرجها من أفضل 250 فيلمًا في البحث عن فيلم ، حتى لا تقوم بذلك يدويًا ، ولكن سيكون ما بين 15-20 فيلمًا كافيًا لتشكيل مجموعة بيانات صغيرة تضم ألف مراجعة لكل فصل. أيضًا ، إذا حصلت على حظر ، فسيقوم البرنامج بعرض الفيلم والفئة التي توقف المحلل اللغوي عن الاستمرار من نفس المكان بعد اجتياز الحظر.
path =
المعالجة
بعد كتابة محلل ، واستدعاء أفلام عشوائية له وعدة عمليات حظر من البحث عن فيلم ، قمت بخلط المراجعات في مجلدات واختيار 900 مراجعة من كل فصل للتدريب والباقي لمجموعة التحكم. الآن من الضروري أن يتم تجهيز السكن مسبقًا ، أي الرمز المميز وتطبيعه. تعني كلمة Tokenizing تقسيم النص إلى مكونات ، في هذه الحالة إلى كلمات ، لأننا سنستخدم تمثيل مجموعة من الكلمات. وتتمثل عملية التطبيع في تحويل الكلمات إلى أحرف صغيرة ، وإزالة كلمات التوقف والضوضاء الزائدة ، والختم وأي حيل أخرى تساعد في تقليل مساحة العلامات.
نحن نستورد المكتبات اللازمة.
النص المخفي from nltk.corpus import PlaintextCorpusReader from nltk.stem.snowball import SnowballStemmer from nltk.probability import FreqDist from nltk.tokenize import RegexpTokenizer from nltk import bigrams from nltk import pos_tag from collections import OrderedDict from sklearn.metrics import classification_report, accuracy_score from sklearn.naive_bayes import MultinomialNB from sklearn.model_selection import GridSearchCV from sklearn.utils import shuffle from multiprocessing import Pool import numpy as np from scipy.sparse import csr_matrix
نبدأ بتعريف بعض الوظائف الصغيرة لعملية تجهيز النصوص. أول كلمة ، تسمى
low_pos_tag ، ستأخذ قائمة تحتوي على كلمات ،
وتحولها إلى أحرف صغيرة ، وستحفظ كل رمز في tuple مع جزء الكلام. تسمى عملية إضافة جزء من الكلام إلى كلمة تمييز جزء من الكلام (POS) وغالبًا ما تستخدم في البرمجة اللغوية العصبية لاستخراج الكيانات. في حالتنا ، سوف نستخدم أجزاء من الكلام في الوظيفة التالية لتصفية الكلمات.
def lower_pos_tag(words): lower_words = [] for i in words: lower_words.append(i.lower()) pos_words = pos_tag(lower_words, lang='rus') return pos_words
تحتوي النصوص على عدد كبير من الكلمات التي يتم العثور عليها في كثير من الأحيان لتكون مفيدة للنموذج (ما يسمى كلمات التوقف). في الأساس ، هذه حروف الجر ، الإقتران ، الضمائر التي يستحيل بواسطتها تحديد الإشارة إلى الفصل. وظيفة
نظيفة يترك فقط الأسماء والصفات والأفعال والظروف. لاحظ أنه يزيل أجزاء من الكلام ، نظرًا لعدم حاجتها للنموذج نفسه. يمكنك أيضًا ملاحظة أن هذه الوظيفة تستخدم التحديق ، وجوهره هو إسقاط اللواحق والبادئات من الكلمات. يتيح لك ذلك تقليل بُعد العلامات ، حيث سيتم اختزال الكلمات ذات الأجناس والحالات المختلفة إلى نفس الرمز. هناك تناظرية أكثر قوة من stamming - lemmatization ، لأنها تتيح لك استعادة الشكل الأولي للكلمة. ومع ذلك ، فإنه يعمل بشكل أبطأ من التحديق ، وبالإضافة إلى ذلك ، لا يحتوي NLTK على جهاز لمبات روسي.
def clean(words): stemmer = SnowballStemmer("russian") cleaned_words = [] for i in words: if i[1] in ['S', 'A', 'V', 'ADV']: cleaned_words.append(stemmer.stem(i[0])) return cleaned_words
بعد ذلك ، نكتب الوظيفة النهائية التي ستأخذ تسمية الفصل واسترداد جميع المراجعات مع هذه الفئة. لقراءة الحالة ، سوف نستخدم الطريقة
الأولية لكائن
PlaintextCorpusReader ، والذي يسمح لك باستخراج النص من الملف المحدد. بعد ذلك ، يتم استخدام الرمز المميز RegexpTokenizer ، ويعمل على أساس تعبير عادي. بالإضافة إلى الكلمات الفردية ، أضفت إلى نموذج bigrams ، والتي هي مزيج من جميع الكلمات المجاورة. تستخدم هذه الوظيفة أيضًا كائن
FreqDist ، والذي يُرجع تكرار حدوث الكلمات. يتم استخدامه هنا لإزالة الكلمات التي تظهر في جميع المراجعات لفئة معينة مرة واحدة فقط (يطلق عليها أيضًا hapaks). وبالتالي ، ستُرجع الدالة قاموسًا يحتوي على مستندات مقدمة كحقيبة من الكلمات وقائمة بجميع الكلمات لفئة معينة.
corpus_root =
تعد مرحلة ما قبل المعالجة هي الأطول ، لذلك فمن المنطقي موازاة معالجة قضيتنا. يمكن القيام بذلك باستخدام وحدة
المعالجة المتعددة . في الجزء التالي من رمز البرنامج ، أبدأ ثلاث عمليات ستقوم في وقت واحد بمعالجة ثلاثة مجلدات بفئات مختلفة. بعد ذلك ، سيتم جمع النتائج في قاموس واحد. اكتمال هذا المعالجة.
if __name__ == '__main__': data = {} labels = ['neutral', 'bad', 'good'] p = Pool(3) result = p.map(process, labels) for i in result: data.update(i) p.close()
كمية موجهة
بعد أن نقوم بمعالجة الحالة مسبقًا ، لدينا قاموس يحتوي فيه تصنيف كل فئة على قائمة تحتوي على مراجعات قمنا برمزها وتطبيعها وإثرائها باستخدام الحروف الكبيرة ، بالإضافة إلى قائمة بالكلمات من جميع مراجعات هذه الفئة. نظرًا لأن النموذج لا يمكنه إدراك اللغة الطبيعية كما نفعل ، فإن المهمة الآن هي تقديم مراجعاتنا في شكل رقمي. للقيام بذلك ، سننشئ مفردات مشتركة ، تتكون من رموز فريدة ، ومعها سنقوم بتوجيه كل مراجعة.
بادئ ذي بدء ، نقوم بإنشاء قائمة تحتوي على مراجعات لجميع الفئات مع تسمياتها. بعد ذلك ، نقوم بإنشاء مفردات مشتركة ، نأخذ من كل فئة 10000 كلمة من أكثر الكلمات شيوعًا باستخدام الطريقة الأكثر شيوعًا في نفس
FreqDist . نتيجة لذلك ، حصلت على مفردات تتكون من حوالي 17000 كلمة.
هناك عدة طرق لتوجيه النص. الأكثر شعبية منهم: TF-IDF ، الترميز المباشر والتردد. لقد استخدمت تشفير التردد ، وجوهره هو تقديم كل مراجعة كمتجه ، وعناصرها هي عدد مرات حدوث كل كلمة من المفردات.
NLTK لها
مصنّفاتها الخاصة ، يمكنك استخدامها ، لكنها تعمل بشكل أبطأ من نظرائها من
scikit-learn ولديها إعدادات أقل. أدناه هو رمز الترميز ل
NLTK . ومع ذلك ، سوف أستخدم نموذج Naive Bayes من
scikit-Learn وأقوم بتشفير المراجعات ، مع توفير السمات في مصفوفة متفرقة من
SciPy ، وتسميات الفصل في صفيف
NumPy منفصل.
نظرًا لأن مجموعة البيانات التي تحتوي على علامات معينة ، في مجموعة البيانات ، تنتقل واحدة تلو الأخرى ، أي أولاً وقبل كل شيء محايد ، وكلها سلبية وغير ذلك ، تحتاج إلى مزجها. للقيام بذلك ، يمكنك استخدام وظيفة
خلط ورق اللعب من
تعلم scikit . إنها مناسبة فقط للمواقف التي تكون فيها العلامات والعلامات الصفية في صفائف مختلفة ، لأنها تتيح لك مزج صفيفين في انسجام تام.
التدريب النموذجي
الآن يبقى تدريب النموذج والتحقق من دقته في مجموعة التحكم. كنموذج ، سوف نستخدم نموذج مصنف Naive Bayes.
لدى Scikit-learn ثلاثة نماذج من Naive Bayes اعتمادًا على توزيع البيانات: ثنائي ، منفصل ، ومستمر. نظرًا لأن توزيع
ميزاتنا منفصل ، فإننا نختار
MultinomialNB .
يحتوي مصنف Bayesian على معامل
alpha hyper ، وهو المسؤول عن تجانس النموذج. تحسب السذاجة بايز احتمالات كل مراجعة تنتمي إلى جميع الفئات ، لهذا ضرب الاحتمالات الشرطية لظهور كل كلمات المراجعة ، شريطة أن تنتمي إلى فئة معينة. ولكن إذا لم يتم العثور على كلمة مراجعة في مجموعة بيانات التدريب ، فإن احتمالها الشرطي يساوي الصفر ، مما يلغي احتمال أن تكون المراجعة تنتمي إلى أي فئة. لتجنب ذلك ، بشكل افتراضي ، تتم إضافة وحدة إلى جميع احتمالات الكلمات الشرطية ، أي أن
alpha تساوي واحدًا. ومع ذلك ، قد لا تكون هذه القيمة هي الأمثل. يمكنك محاولة تحديد
ألفا باستخدام شبكة البحث والتقاطع التحقق من الصحة.
parameter = [1, 0, 0.1, 0.01, 0.001, 0.0001] param_grid = {'alpha': parameter} grid_search = GridSearchCV(MultinomialNB(), param_grid, cv=5) grid_search.fit(X, Y) Alpha, best_score = grid_search.best_params_, grid_search.best_score_
في حالتي ، يعطي موقد الشبكة القيمة المثلى لمقياس التشويش الفائق الذي يساوي 0 بدقة 0.965. ومع ذلك ، من الواضح أن هذه القيمة لن تكون مثالية لمجموعة بيانات التحكم ، حيث سيكون هناك عدد كبير من الكلمات التي لم يتم العثور عليها مسبقًا في مجموعة التدريب. بالنسبة لمجموعة البيانات المرجعية ، يتمتع هذا النموذج بدقة 0.598. ومع ذلك ، إذا قمت بزيادة
ألفا إلى 0.1 ، فإن دقة بيانات التدريب ستنخفض إلى 0.82 ، وفي بيانات التحكم ستزداد إلى 0.62. على الأرجح ، في مجموعة بيانات أكبر ، سيكون الفرق أكثر أهمية.
model = MultinomialNB(0.1) model.fit(X, Y)
استنتاج
من المفترض أن يتم استخدام النموذج للتنبؤ بالمراجعات التي لم يتم استخدام كلماتها لتشكيل المفردات. لذلك ، يمكن تقييم جودة النموذج من خلال دقته في جزء التحكم في البيانات ، وهو 0.62. هذا أفضل مرتين تقريبًا من مجرد التخمين ، لكن الدقة لا تزال منخفضة جدًا.
وفقًا لتقرير التصنيف ، من الواضح أن النموذج يعمل بشكل أسوأ مع المراجعات التي لها لون محايد (دقة 0.47 مقابل 0.68 للإيجابية و 0.76 للإيجابية). في الواقع ، تحتوي المراجعات المحايدة على كلمات مميزة لكل من المراجعات الإيجابية والسلبية. ربما ، يمكن تحسين دقة النموذج من خلال زيادة حجم مجموعة البيانات ، لأن مجموعة البيانات الثلاثة آلاف هي متواضعة إلى حد ما. أيضًا ، سيكون من الممكن تقليل المشكلة إلى تصنيف ثنائي للمراجعات إلى إيجابية وسلبية ، مما يزيد من الدقة أيضًا.
شكرا للقراءة.
ملاحظة: إذا كنت ترغب في ممارسة نفسك ، يمكن تنزيل مجموعة البيانات الخاصة بي أسفل الرابط.
رابط إلى مجموعة البيانات