Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save mabras/db9a9d5d7f2e3742f6e20617d5831e47 to your computer and use it in GitHub Desktop.
Save mabras/db9a9d5d7f2e3742f6e20617d5831e47 to your computer and use it in GitHub Desktop.
Sentiment Analysis with the sentimental Gem (Arabic)

#التحليل العاطفي الحاسوبي

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

ما نعني بالتحليل العاطفي هو: هل صاحب التغريدة يحب أو يكره هذا الشيء أم هو حيادي حوله، وسيكون ذلك عن طريق أخذ أي نص وتحليله وتقدير مدى إيجابية أو سلبية هذا النص. سنعتمد على مكتبة (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 وذلك بما يتوافق مع مُتطلبات التطبيق الذي تعمل عليه فكل ما تقدمه المكتبة هو نقطة بداية والتي يمكنك الانطلاق منها وتطويرها على حسب الحاجة.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment