#التحليل العاطفي الحاسوبي
سنتحدّث في الدرس عن التحليل العاطفي الحاسوبي، حيث أننا سنقوم بتحميل جملة من التغريدات إلى تطبيق "ريلز" وبالتحديد نص التغريدة لنحدد مدى إيجابية وسلبية هذه التغريدات بناء على محتواها وكلماتها.
ما نعني بالتحليل العاطفي هو: هل صاحب التغريدة يحب أو يكره هذا الشيء أم هو حيادي حوله، وسيكون ذلك عن طريق أخذ أي نص وتحليله وتقدير مدى إيجابية أو سلبية هذا النص. سنعتمد على مكتبة (gem) معينة في تنفيذ الفكرة وهي مكتبة sentimental
. الجدير بالذكر هو وجود العديد من الأمور التي يجب أخذها بعين الاعتبار لتنفيذ الفكرة، فالحديث عن أي لغة ليس بالأمر الهين فيجب التميز بين التصريح المقصود وبين التهكّم والذي قد يكون من الصعب الكشف وتفسيره المقصود منه بالضبط، ولكن في المجمل سنحصل على دقة مرضية بناء على العدد الإجمالي للكمات المحلّلّة من التغريدات ككل.
فعلى السبيل المثال عند البحث عن كلمة Microsoft سيكون هناك معايرة بين فحوى التغريدات بين من يقول "أحب ميكروسوفت" وبين من يقول "أكره شركة ميكروسوفت" وعليه سيتمّ الحكم على كل تغريدة إما أنها إيجابية أو سلبية.
تأتي مكتبة sentimental
بقائمة من الكلمات، وتحمل كل كلمة قيمة بين 1.0
و -1.0
وذلك لتحديد إيجابية وسلبية كلٍ من هذه الكلمات. كما يمكن التعديل على هذه القائمة وإضافة ما يلزم.
سنبدأ بإنشاء تطبيق Rails وجلب بعض التغريدات إلى قاعدة البيانات وذلك بإستخدام مكتبة تويتر ومن ثم التعامل مع هذه التغريدات على الخادم المحلّي وذلك لتسهيل عملية اختبارها ليس إلا.
rails new sentimental_tweets
cd sentimental_tweets
إضافة مكتبة تويتر إلى Gemfile
:
#Gemfile
gem ‘twitter’, ‘~> 5.16’
والتنصيب:
bundle install
خطوتنا التالية ستكون إنشاء model من أجل التغريدات، ولكن أولا سنتوجه إلى توثيق مكتبة sentimental
لنحدّد شكل هذا "الموديل". حيث تقوم المكتبة بتحليل النص المُعطى والرد إما positive
أو neutral
أو negative
حيث أن القيمة الحيادية ما هي إلا القيمة 0
بشكل افترضي والتي يمكن تغيرها. إن أي تحليل ولأي تغريدة يحصل على رقم معيّن بناء على شدة الإيجابية أو السلبية وهو الرقم المعبّر عنه بالتعبير score
وبطبيعة الحال هذا الرقم هو الرقم الدقيق والذي يعطي كل تغريدة قيمتها الحقيقية بدلا من المصطلحات العامة الأنفة الذكر (إيجابي – سلبي – حيادي) وهذا الرقم يمكننا حفظه كنسخة مخبّئة (cached) بدلا من طلب تحليل جديد في كل مرّة نريد تحليل التغريدة فيه.
rails generate scaffold Tweet body:text sentiment:string score:decimal
rake db:migrate
سنعتمد الآن على مكتبة تويتر بغرض البحث عن التغريدات المراد البحث عنها، وبالعودة إلى دليل الاستخدام الخاص بالمكتبة يمكننا إعداداها بالإعدادات الأساسية:
class Tweet < ApplicationRecord
def self.sync(query)
client = Twitter::REST::Client.new do |config|
config.consumer_key = Rails.application.secrets.consumer_key
config.consumer_secret = Rails.application.secrets.consumer_secret
config.access_token = Rails.application.secrets.access_token
config.access_token_secret = Rails.application.secrets.access_token_secret
end
client.search(query)
end
end
سنجرّب الآن الشيفرة السابقة:
Tweet.sync(“rails”)
الاستعلام السابق سيخرج عدد كبير من التغريدات وسنقوم بالخطوة التالية بحفظ هذه التغريدات في قاعدة البيانات بعد جلبها من موقع تويتر:
class Tweet < ApplicationRecord
def self.sync(query)
client = Twitter::REST::Client.new do |config|
config.consumer_key = Rails.application.secrets.consumer_key
config.consumer_secret = Rails.application.secrets.consumer_secret
config.access_token = Rails.application.secrets.access_token
config.access_token_secret = Rails.application.secrets.access_token_secret
end
client.search(query).each do |tweet|
create(body: tweet.text)
end
end
end
والآن سنعيد تنفيذ المزامنة السابقة وذلك في جلب التغريدات ومن ثم حفظها في قاعدة البيانات:
#rails console
Tweet.sync(“rails”)
أما خطوتنا التالية ستكون تحليل هذه التغريدات باستخدام مكتبة sentimental
والتي حتى الآن لم نقم بإضافتها إلى التطبيق:
#Gemfile
gem ‘sentimental`, ‘~> 1.2’, ‘>= 1.2.1’
bundle install
سنقوم بإنشاء initializer
خاص بالمكتبة ليتم تحميله مع بداية إقلاع التطبيق وسنجعله global لكي يتمّ تحميله مرّة واحدة فقط.
#config/initializers/sentimental.rb
$analyzer = Sentimental.new
$analyzer.load_defaults
لنقم الآن بتجربة التحليل العاطفي:
#rails console
$analyzer.sentiment Tweet.last.body
=> :positive
أما إذا أردنا معرفة القيمة الحقيقية فيجب استخدام الدالة score
:
$analyzer.score Tweet.last.body
=> 0.650
والأن كيف لنا إضافة هذا التحليل إلى التطبيق. حيث يوجد طُرق عدة لإتمام ذلك، لكن لا يُنصح بإضافة نتيجة التحليل بشكل مُباشر:
client.search(query).each do |tweet|
create(body: tweet.text, sentimental: $analyzer.sentiment(tweet.text))
end
بل الأفضل استخدام callback وعليه في حال كان التحليل بطيء ويستهلك وقتا عندها يمكن الاستعانة background job أي مهام تعمل في الخلفية مما يؤدي إلى أداء سلس في التطبيق وتجربة استخدام أفضل. كما سيسمح لنا هذا الأسلوب بالحصول على تحليل جديد تلقائيا عند إجراء أي تحديث على نص التغريدة.
class Tweet < ApplicationRecord
def self.sync(query)
client = Twitter::REST::Client.new do |config|
config.consumer_key = Rails.application.secrets.consumer_key
config.consumer_secret = Rails.application.secrets.consumer_secret
config.access_token = Rails.application.secrets.access_token
config.access_token_secret = Rails.application.secrets.access_token_secret
end
client.search(query).each do |tweet|
create(body: tweet.text)
end
end
before_save :set_sentiment, if: :body_changed?
def set_sentiment
self.sentiment = $analyzer.sentiment(body)
self.score = $analyzer.score(body)
end
end
كما هو واضح من الشيفرة السابقة سيتمّ إجراء التحليل عندما يتمّ أي تغيّر على متن الرسالة وذلك قبل الحفظ before_save
.
#rails console
Tweet.sync(“ruby”)
Tweet.last.sentiment
#=> “positive”
Tweet.last.score.to_f
#=>0.2813
سنقوم في الخطوة التالية بإنشاء scope
للاستعلام عن التغريدات الإيجابية والسلبية والحيادية:
class Tweet < ApplicationRecord
def self.sync(query)
client = Twitter::REST::Client.new do |config|
config.consumer_key = Rails.application.secrets.consumer_key
config.consumer_secret = Rails.application.secrets.consumer_secret
config.access_token = Rails.application.secrets.access_token
config.access_token_secret = Rails.application.secrets.access_token_secret
end
client.search(query).each do |tweet|
create(body: tweet.text)
end
end
before_save :set_sentiment, if: :body_changed?
scope :positive, ->{ where(sentiment: :positive) }
scope :neutral, ->{ where(sentiment: :neutral) }
scope :negative, ->{ where(sentiment: :negative) }
def set_sentiment
self.sentiment = $analyzer.sentiment(body)
self.score = $analyzer.score(body)
end
end
لنستعرض الآن عدد التغريدات الإيجابية والسلبية الحيادية باستخدام الـ scope
المنشئة:
Tweet.positive.count
=>499
Tweet.neutral.count
=>120
Tweet.negative.count
=>181
وهكذا أصبح بإمكاننا البحث أي كلمة للحصول على تحليل لها وذلك بناء على ردود أفعال المغردين. والجدير بالذكر أنه من المستحسن الإضافة على قاعدة بيانات مكتبة sentimental
وذلك بما يتوافق مع مُتطلبات التطبيق الذي تعمل عليه فكل ما تقدمه المكتبة هو نقطة بداية والتي يمكنك الانطلاق منها وتطويرها على حسب الحاجة.