12/08/2018, 18:26

Xây dựng một hệ thống gợi ý Collaborative Filtering dễ dàng như thế nào?

Hệ thống gợi ý là một hệ thống sử dụng các dữ liệu thu thập được từ người dùng nhằm dự đoán, gợi ý cho người dùng những sản phẩm, tính năng, dịch vụ mà người dùng có thể thích, từ đó nâng cao được chất lượng dịch vụ và thu lại lợi nhuận. Vậy cách để thực hành xây dựng một hệ thống gợi ý như thế ...

Hệ thống gợi ý là một hệ thống sử dụng các dữ liệu thu thập được từ người dùng nhằm dự đoán, gợi ý cho người dùng những sản phẩm, tính năng, dịch vụ mà người dùng có thể thích, từ đó nâng cao được chất lượng dịch vụ và thu lại lợi nhuận. Vậy cách để thực hành xây dựng một hệ thống gợi ý như thế nào? Bài viết này mình sẽ hướng dẫn các bạn xây dựng một hệ thống gợi ý đơn giản nhưng khá là hiệu quả trong thực tế, chỉ cần bạn là một người biết code cơ bản, chỉ cần bạn làm theo những gì mà mình hướng dẫn dưới đây, bạn sẽ xây dựng được một hệ thống gợi ý sản phẩm có thể áp dụng trực tiếp vào việc buôn bán thực tế của mình hay người thân. Nào, hãy thử làm xem sao!

Khi bạn đánh giá một trang fanpage trên facebook, hay đánh giá một sản phẩm ở một trang web mua sắm nào đó, chúng ta thường có các đánh giá dưới dạng ratings có điểm số từ 1 đến 5 hay 1 đến 7. Chúng ta sẽ dựa trên các đánh giá này của người dùng lên các sản phẩm để đưa ra gợi ý mua sắm cho người dùng. Một ví dụ của bảng ratings mà chúng ta nhận được như sau:

User/Item 0 1 2 3 4 5
0 7 6 7 4 5 4
1 6 7 ? 4 3 4
2 ? 3 3 1 1 ?
3 1 2 2 3 3 4
4 1 ? 2 2 3 3

Dấu hỏi ở đây đại diện cho những rating mà người dùng chưa đánh giá cho sản phẩm, việc làm của chúng ta là cần phải dự đoán giá trị trong các dấu hỏi này. Nếu giá trị dự đoán được là cao, có khả năng cao rằng người dùng sẽ thích sản phẩm đó, và chúng ta đưa ra gợi ý cho người dùng.

Ý tưởng cơ bản của User-based Collaborative Filtering như sau:

  • Giả sử trong quá khứ của người dùng A, người này thích giày thể thao hiệu XYZ, mũ hiệu UVT, kính râm hiệu MNP. Cũng tương tự trong quá khứ của B, B cũng thích giày thể thao hiệu XYZ, mũ hiệu UVT. Ta nhận thấy rằng sở thích của hai người này khá là giống nhau, do A có thích thêm kính râm hiệu MNP, có khả năng cao rằng B cũng thích kính râm hiệu MNP này, ta sẽ gợi ý cho B mua sản phẩm này.

Phương pháp thực hiện

Công việc mà chúng ta cần làm để dự đoán được sở thích của một người dùng uuu lên một sản phầm iii gồm 2 bước như sau:

  1. Đầu tiên, chúng ta phải tìm được nhóm người có chung sở thích với người dùng uuu này thông qua bảng ratings mà chúng ta đã có.
  2. Sau đó, dựa trên nhóm người mà chúng ta có chung sở thích với uuu mà chúng ta tìm được và các đánh giá của nhóm người này lên sản phẩm iii, chúng ta sẽ dự đoán đánh giá của người dùng uuu lên sản phẩm iii.

Quy trình khá là đơn giản phải không?

Trước tiên chúng ta định nghĩa ma trận ratings ở trên:

import numpy as 

nan = np.nan # not a number đại diện cho các sản phẩm chưa được đánh giá
ratings_matrix = np.array([[7, 6, 7, 4, 5, 4], 
                           [6, 7, nan, 4, 3, 4],
                           [nan, 3, 3, 1, 1, nan],
                           [1, 2, 2, 3, 3, 4],
                           [1, nan, 1, 2, 3, 3]])

Ma trận rating của chúng ta có kích thước là m×nm imes nm×n, ký hiệu là R=[ruj]R = [r_{uj}]R=[ruj], trong đó, mmm là số lượng người dùng (user) và nnn là số lượng sản phẩm (items); rujr_{uj}ruj là điểm đánh giá của user uuu lên item jjj. Ví dụ ở trong bảng trên ta có r10=6r_{10} = 6r10=6 là điểm đánh giá của user 1 1 1 cho item 0 0 0. Chúng ta ký hiệu IuI_uIu là tập hợp các sản phẩm mà đã được đánh giá bởi user uuu, khi đó dựa theo bảng trên ta có I2=1,2,3,4I_2 = {1, 2, 3, 4}I2=1,2,3,4 . Tập hợp các sản phẩm được đánh giá bởi cả hai người dùng uuuvvv được ký hiệu là Iu∩IvI_u cap I_vIuIv

# indices for vector
def specified_rating_indices(u):
    return list(map(tuple, np.where(np.isfinite(u))))

Có một lưu ý nhỏ ở đây khi nói về đặc điểm cá nhân của người dùng: có những người dùng dễ tính thông thường sẽ đánh giá điểm cho các sản phẩm cao hơn so với những người dùng khó tính. Vì vậy, để khách quan hơn, chúng ta sẽ trừ điểm đánh giá của mỗi người dùng một lượng bằng trung bình điểm đánh giá của người dùng đó. Điểm trung bình của người dùng đó được tính như sau:

μu=Σk∈Iuruk∣Iu∣mu_u = dfrac{Sigma_{k in I_u} r_{uk}}{|I_u|} μu=IuΣkIuruk

def mean(u):
    # may use specified_rating_indices but use more time
    specified_ratings = u[specified_rating_indices(u)]#u[np.isfinite(u)]
    m = sum(specified_ratings)/np.shape(specified_ratings)[0]
    return m
def all_user_mean_ratings(ratings_matrix):
    return np.array([mean(ratings_matrix[u, :]) for u in range(ratings_matrix.shape[0])])
def get_mean_centered_ratings_matrix(ratings_matrix):
    users_mean_rating = all_user_mean_ratings(ratings_matrix)
    mean_centered_ratings_matrix = ratings_matrix - np.reshape(users_mean_rating, [-1, 1])
    return mean_centered_ratings_matrix

Ta có được ma trận sau khi đã trừ đi trung bình ratings như sau:

mean_centered_ratings_matrix = get_mean_centered_ratings_matrix(ratings_matrix)
# array([[ 1.5,  0.5,  1.5, -1.5, -0.5, -1.5],
         [ 1.2,  2.2,  nan, -0.8, -1.8, -0.8],
         [ nan,  1. ,  1. , -1. , -1. ,  nan],
         [-1.5, -0.5, -0.5,  0.5,  0.5,  1.5],
         [-1. ,  nan, -1. ,  0. ,  1. ,  1. ]])

Để tính được sự giống nhau giữa các người dùng, chúng ta sẽ sử dụng điểm đánh giá của người dùng đó lên tất cả các sản phẩm làm vector đại diện cho người dùng, rồi so sánh các vector với nhau để tìm sự giống nhau. Có nhiều hàm toán học để tính toán độ giống nhau giữa các vector như Pearson, Cosine, ... Ở đây chúng ta sử dụng Pearson (code của hàm này khác phức tạp, bạn có thể bỏ qua hàm này), hàm này được xây dựng như sau:

Sim(u,v)=Pearson(u,v)=Σk∈Iu∩Iv(ruk−μu)(rvk−μv)Σk∈Iu∩Iv(ruk−μu)2Σk∈Iu∩Iv(rvk−μv)2Sim(u, v) = Pearson(u, v) = dfrac{Sigma_{k in I_u cap I_v}(r_{uk} - mu_u)(r_{vk} - mu_v)}{sqrt{Sigma_{k in I_u cap I_v}(r_{uk} - mu_u)^2}sqrt{Sigma_{k in I_u cap I_v}(r_{vk} - mu_v)^2}} Sim(u,v)=Pearson(u,v)=ΣkIuIv(rukμu)2ΣkIuIv(rvkμv)2ΣkIuIv(rukμu)(rvkμv)

Ví dụ:

pearson(user0,user2)=(6−5.5)∗(3−2)+(7−5.5)∗(3−2)+(4−5.5)∗(1−2)+(5−5.5)∗(1−2)1.52+1.52+(−1.5)2+(−0.5)2.12+12+(−1)2+(−1)2=0.894pearson(user_0, user_2) = dfrac{(6 - 5.5) * (3 - 2) + (7 - 5.5) * (3 - 2) + (4 - 5.5) * (1 - 2) + (5 - 5.5) * (1 - 2)}{sqrt{1.5^2 + 1.5^2 + (-1.5)^2 + (-0.5)^2}.sqrt{1^2 + 1^2 + (-1)^2 + (-1)^2}} = 0.894 pearson(user0,user2)=1.52+1.52+(1.5)2+(0.5)2.12+12+(1)2+(1)2(65.5)(32)+(75.5)(32)+(45.5)(12)+(55.5)(12)=0.894

def pearson(u, v):
    mean_u = mean(u)
    mean_v = mean(v)
    
    specified_rating_indices_u = set(specified_rating_indices(u)[0])
    specified_rating_indices_v = set(specified_rating_indices(v)[0])
    
    mutually_specified_ratings_indices = specified_rating_indices_u.intersection(specified_rating_indices_v)
    mutually_specified_ratings_indices = list(mutually_specified_ratings_indices)
    
    u_mutually = u[mutually_specified_ratings_indices]
    v_mutually = v[mutually_specified_ratings_indices]
      
    centralized_mutually_u = u_mutually - mean_u
    centralized_mutually_v             
0