Intro to Machine Learning in Ruby
Bài đăng này đã không được cập nhật trong 7 năm
Machine Learning(học máy) là một nhánh của Artificial Intelligence(AI - trí tuệ nhân tạo) liên quan tới thiết kế và phát triển thuật toán cho phép máy tính có thể xử lý và học được thông tin. Đó là một chủ đề vô cùng rộng lớn do đó chúng ta sẽ chỉ tập trung vào một ví dụ đơn giản sử dụng thuật ngữ phân loại thống kê.
Let's build...
Trong ví dụ dưới đây chúng ta sẽ xây dựng một ứng dụng phân loại thống kê sẽ phân tích và phân loại các bài viết RSS/HTML từ trang báo Times Live Để làm công việc này, chúng ta sẽ sử dụng gem nokogiri và 2 chuẩn thư viện: open-uri và rss/2.0
RSS Parser
Để tìm nguồn của các bài viết để xử lý, chúng ta có thể xây dựng một công nghệ tìm kiếm phức tạp hoặc đơn giản là sử dụng RSS để lấy những bài báo cung cấp cho chúng ta từ một nguồn nào đó. RssParser làm điều đó, bạn khởi tạo với một địa chỉ url và nó sẽ trả về tất cả các liên kết tới tất cả các bài viết mà nó tìm thấy được từ nguồn url đó.
class RssParser
attr_accessor :url
def initialize(url)
@url = url
end
def article_urls
RSS::Parser.parse(open(url), false).items.map{|item| item.link }
end
end
HTML Parser
Khi đã có các liên kết, chúng ta cần phân tích nội dung và trích xuất ra những phần có nghĩa từ những trang đó. HtmlParser có thể khởi tạo với một liên kết tới một trang và dùng DOM selector để làm điều đó. Trong ví dụ này, chúng ta sẽ sử dụng một CSS selector để lấy ra nội dung từ một bài viết - Firebug và jQuery trước đó cũng được sử dụng để trích xuất nội dung từ các bài viết. Trong trường hợp này, chúng ta cũng sẽ sử dụng phương thức clean_whitespace để xóa bỏ những ký tự rỗng từ văn bản trích xuất
class HtmlParser
attr_accessor :url, :selector
def initialize(url, selector)
@url = url
@selector = selector
end
def content
doc = Nokogiri::HTML(open(url))
html_elements = doc.search(selector)
html_elements.map { |element| clean_whitespace(element.text) }.join(' ')
end
private
def clean_whitespace(text)
text.gsub(/\s{2,}|\t|\n/, ' ').strip
end
end
Statistical Classifier
Chúng ta sẽ đến với một class có trách nhiệm phân loại các bài viết. Nó được khởi tạo với một mảng chứa các keys của các loại bài viết mà chúng ta sẽ phân loại vào và dữ liệu huấn luyện training với các loại tương ứng. Dữ liệu huấn luyện được sử dụng để khám phá ra mối quan hệ giữa bài viết và thể loại của bài viết đó. Dữ liệu này nên được chọn lọc kỹ và sắp xếp theo thứ tự để có thể có được kết quả phân loại tốt hơn. Nó được tạo ra bởi quyết định giá trị của từng từ trong một ngữ cảnh của tất cả các từ trong mỗi một danh mục bài viết phân loại. Trong ví dụ dưới đây, chúng ta sẽ sử dụng các bài viết Wikipedia để huấn luyện dữ liệu với ba danh mục là kinh tế, thể thao và sức khỏe. Trong khi phân loại các bài viết, chúng ta sẽ chỉ so sánh các từ có nghĩa và bỏ qua các từ khác mà không thêm vào bất kỳ giá trị nào cho mỗi danh mục phân loại. Chúng ta giải quyết vấn đề sử dụng các từ cố định stop words Cuối cùng phương thức scores() sẽ đánh giá tạo điểm với mỗi một danh mục phân loại mà chúng ta kiểm tra
class Classifier
attr_accessor :training_sets, :noise_words
def initialize(data)
@training_sets = {}
filename = File.join(File.dirname(__FILE__), 'stop_words.txt')
@noise_words = File.new(filename).readlines.map(&:chomp)
train_data(data)
end
def scores(text)
words = text.downcase.scan(/[a-z]+/)
scores = {}
training_sets.each_pair do |category, word_weights|
scores[category] = score(word_weights, words)
end
scores
end
def train_data(data)
data.each_pair do |category, text|
words = text.downcase.scan(/[a-z]+/)
word_weights = Hash.new(0)
words.each {|word| word_weights[word] += 1 unless noise_words.index(word)}
ratio = 1.0 / words.length
word_weights.keys.each {|key| word_weights[key] *= ratio}
training_sets[category] = word_weights
end
end
private
def score(word_weights, words)
score = words.inject(0) {|acc, word| acc + word_weights[word]}
1000.0 * score / words.size
end
end
Lets have a go
Dưới đây là đoạn script chạy trương trình
require 'rubygems'
require 'nokogiri'
require 'open-uri'
require 'rss/2.0'
# training data samples
economy = HtmlParser.new('http://en.wikipedia.org/wiki/Economy', '.mw-content-ltr')
sport = HtmlParser.new('http://en.wikipedia.org/wiki/Sport', '.mw-content-ltr')
health = HtmlParser.new('http://en.wikipedia.org/wiki/Health', '.mw-content-ltr')
training_data = {
:economy => economy.content,
:sport => sport.content,
:health => health.content
}
classifier = Classifier.new(training_data)
results = {
:economy => [],
:sport => [],
:health => []
}
rss_parser = RssParser.new('http://avusa.feedsportal.com/c/33051/f/534658/index.rss')
rss_parser.article_urls.each do |article_url|
article = HtmlParser.new(article_url, '#article .area > h3, #article .area > p, #article > h3')
scores = classifier.scores(article.content)
category_name, score = scores.max_by{ |k,v| v }
# DEBUG info
# p "category: #{category_name}, score: #{score}, scores: #{scores}, url: #{article_url}"
results[category_name] << article_url
end
p results
Mặc dù thuật toán phân loại rất đơn giản, nó cũng có thể mang lại kết quả đáng lưu ý cung cấp huấn luyện dữ liệu tốt. Để có thể có kết quả tốt hơn, bạn có thể thử một vài thuật toán phân loại khác giống như Bayesian probability và Latent semantic analysis
Refs
All rights reserved