Xây dựng hệ gợi ý với Machine Learning bằng Laravel
Người viết: Ngô Thuý Hoa Ở bài này, chúng ta sẽ xây dựng 1 hệ gợi ý item-based từ đầu. Chúng ta sẽ sử dụng machine learning để so sánh độ tương đồng giữa 2 sản phẩm trong bộ dữ liệu sử dụng trong bài. Với machine learning, bạn có thể có rất nhiều giải thuật để áp dụng và việc lựa ...
Người viết: Ngô Thuý Hoa
Ở bài này, chúng ta sẽ xây dựng 1 hệ gợi ý item-based từ đầu. Chúng ta sẽ sử dụng machine learning để so sánh độ tương đồng giữa 2 sản phẩm trong bộ dữ liệu sử dụng trong bài.
Với machine learning, bạn có thể có rất nhiều giải thuật để áp dụng và việc lựa chọn sẽ phụ thuộc vào bộ data của bạn và mục đích của bạn. Vì vậy hãy thử vài thuật toán để lựa chọn ra thuật toán tốt nhất cho mục đích của bạn.
Ở ứng dụng demo này sẽ có 3 giải thuật được sử dụng.
Nhóm giải thuật Giám sát và Không giám sát
Cách giải thuật trong machine learning được chia làm 2 nhóm: học Giám sát và học Không giám sát.
Học giám sát được sử dụng khi bạn đánh nhãn dữ liệu và bạn biết chính xác kết quả đầu ra. Nó kiểu kiểu như 1 giáo viên sửa lại các kết quả của thuật toán cho tới khi nó cho kết quả chấp nhận được.
Học không giám sát là khi dữ liệu không được gán nhãn và bạn sẽ không biết kết quả chính xác của dữ liệu là gì. Thuật toán sẽ đưa ra và định dạng dữ liệu và qua đó bạn có thể phát hiện các đặc điểm và hiểu rõ hơn về dữ liệu.
Lọc cộng tác hướng người dùng và lọc cộng tác hướng sản phẩm
Có 2 cách để biểu diễn sự gợi ý cho người dùng:
Cách đầu tiên là hướng người dùng(user-based). Cách này hiểu đơn giản là bạn và 1 anh hàng xóm cùng thích chung 1 số hoạt hình như Doraemon, One Piece, …. và khi anh hàng xóm bắt đầu xem Siêu nhân Gao thì bạn cũng sẽ được giới thiệu để xem phim ấy.
Cách còn lại là hướng sản phẩm(item-based). Cách này thì không dựa vào việc người khác thích gì mà dựa vào việc bạn thích gì và hệ thống sẽ gợi ý ra các sản phẩm có đặc tính tương tự. Ví dụ như bạn thích Siêu nhân Gao thì hệ thống sẽ có các phim Siêu nhân Khủng Long, Siêu nhân Cuồng phong, Siêu nhân Thần kiếm, Siêu nhân Đạo chính choảng Siêu nhân Cảnh sát,…..
Xây dựng ứng dụng
Với app thử nghiệm ở mục này, chúng ta sẽ xây dựng hệ gợi ý hướng sản phẩm với nhóm thuật toán Giám sát.
Chúng ta sử dụng các đặc tính của sản phẩm, trường hợp này là thời trang (chất liệu, màu sắc, độ ấm,…), giá và loại sản phẩm làm cơ sở cho việc tính toán độ tương đồng.
Các thuật toán sử dụng là Hamming Distance cho đề xuất sản phẩm, Euclidean Distance cho giá của sản phầm và Chỉ mục Jacard cho phân loại sản phẩm.
Bước 1
Tạo project Laravel mới:
1 2 3 |
composer create-project --prefer-dist laravel/laravel laravel-recommender-system |
Bước 2
Lấy bộ dữ liệu ở link dưới đây và lưu vào ./storage/data trong project Laravel của bạn.
Nếu bạn định sử dụng cho môi trường production thì lời khuyên đặc biệt trong trường hợp này là bạn hãy làm các model Eloquent và seed data vào cơ sở dữ liệu thay vì dùng JSON.
Xem thêm JSON là gì
Bước 3
Ở đây 1 class chứa toàn bộ các thuật toán cần thiết được tạo ra để tính độ tương đồng. Copy code dưới đây và đặt vào Similarity.php trong ./app.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 |
<?php declare(strict_types=1); namespace App; class Similarity { public static function hamming(string $string1, string $string2, bool $returnDistance = false): float { $a = str_pad($string1, strlen($string2) - strlen($string1), ' '); $b = str_pad($string2, strlen($string1) - strlen($string2), ' '); $distance = count(array_diff_assoc(str_split($a), str_split($b))); if ($returnDistance) { return $distance; } return (strlen($a) - $distance) / strlen($a); } public static function euclidean(array $array1, array $array2, bool $returnDistance = false): float { $a = $array1; $b = $array2; $set = []; foreach ($a as $index => $value) { $set[] = $value - $b[$index] ?? 0; } $distance = sqrt(array_sum(array_map(function ($x) { return pow($x, 2); }, $set))); if ($returnDistance) { return $distance; } // doesn't work well with distances larger than 1 // return 1 / (1 + $distance); // so we'll use angular similarity instead return 1 - $distance; } public static function jaccard(string $string1, string $string2, string $separator = ','): float { $a = explode($separator, $string1); $b = explode($separator, $string2); $intersection = array_unique(array_intersect($a, $b)); $union = array_unique(array_merge($a, $b)); return count($intersection) / count($union); } public static function minMaxNorm(array $values, $min = null, $max = null): array { $norm = []; $min = $min ?? min($values); $max = $max ?? max($values); foreach ($values as $value) { $numerator = $value - $min; $denominator = $max - $min; $minMaxNorm = $numerator / $denominator; $norm[] = $minMaxNorm; } return $norm; } } |
Bước 4
Khi đã có 1 class chứa tất cả các giải thuật thì chúng ta cũng cần 1 class chứa các phép tính độ tương đồng. Tất cả code dưới đây là trong ProductSimilarity.php ở thư mục ./app.
Phương thức calculateSimilarityMatrix sẽ tính toán sự giống nhau giữa tất cả các sản phẩm và tạo ra một ma trận.
Nếu bạn định sử dụng hệ gợi ý này trong production hoặc nếu bạn có nhiều sản phẩm, bạn nên tạo một lệnh Artisan gọi chức năng này tuần tự thông qua Scheduler và lưu trữ kết quả trong Redis hoặc các cơ sở dữ liệu NoSQL tương tự.
Bằng cách đó, bạn có ma trận tương tự đầy đủ được lưu trong bộ nhớ cache và không phải tính toán nó mỗi khi có request.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 |
<?php declare(strict_types=1); namespace App; use Exception; class ProductSimilarity { protected $products = []; protected $featureWeight = 1; protected $priceWeight = 1; protected $categoryWeight = 1; protected $priceHighRange = 1000; public function __construct(array $products) { $this->products = $products; $this->priceHighRange = max(array_column($products, 'price')); } public function setFeatureWeight(float $weight): void { $this->featureWeight = $weight; } public function setPriceWeight(float $weight): void { $this->priceWeight = $weight; } public function setCategoryWeight(float $weight): void { $this->categoryWeight = $weight; } public function calculateSimilarityMatrix(): array { $matrix = []; foreach ($this->products as $product) { $similarityScores = []; foreach ($this->products as $_product) { if ($product->id === $_product->id) { continue; } $similarityScores['product_id_' . $_product->id] = $this->calculateSimilarityScore($product, $_product); } $matrix['product_id_' . $product->id] = $similarityScores; } return $matrix; } public function getProductsSortedBySimularity(int $productId, array $matrix): array { $similarities = $matrix['product_id_' . $productId] ?? null; $sortedProducts = []; if (is_null($similarities)) { throw new Exception('Can't find product with that ID.'); } arsort($similarities); foreach ($similarities as $productIdKey => $similarity) { $id = intval(str_replace('product_id_', ', $productIdKey)); $products = array_filter($this->products, function ($product) use ($id) { return $product->id === $id; });
Có thể bạn quan tâm
0
|