Intro to Machine Learning in Ruby
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ữ ...
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,}| | /, ' ').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
Intro to Machine Learning in Ruby