{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"pygments_lexer":"ipython3","nbconvert_exporter":"python","version":"3.6.4","file_extension":".py","codemirror_mode":{"name":"ipython","version":3},"name":"python","mimetype":"text/x-python"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":31254,"databundleVersionId":3103714,"sourceType":"competition"}],"dockerImageVersionId":31193,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"# =============================================================================\n# === NOTEBOOK #1: XÂY DỰNG VÀ LƯU BẢN ĐỒ RECALL (BATCH & CÓ SCORE) ===\n# =============================================================================\n# Mục tiêu: Chắc chắn chạy thành công trên toàn bộ dữ liệu VÀ LƯU CẢ ĐIỂM SỐ.\n# Cách chạy: Sử dụng \"Save & Run All (Commit)\".\n# =============================================================================\n\n# === BƯỚC 0: CÀI ĐẶT ===\nprint(\"Đang cài đặt các thư viện cần thiết...\")\n!pip install -q sentence-transformers tqdm\nprint(\"Cài đặt hoàn tất.\")\n\n# === BƯỚC 1: TẢI THƯ VIỆN VÀ DỮ LIỆU ===\nimport pandas as pd\nimport numpy as np\nfrom datetime import timedelta\nfrom collections import defaultdict\nimport warnings\nfrom tqdm.auto import tqdm\nimport pickle\nimport gc\n\nwarnings.filterwarnings('ignore')\n\nfrom sklearn.feature_extraction.text import TfidfVectorizer\nfrom sklearn.neighbors import NearestNeighbors\nfrom sentence_transformers import SentenceTransformer\n\nprint(\"Các thư viện đã sẵn sàng.\")\n\n# Tải dữ liệu\ntransactions = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/transactions_train.csv', dtype={'article_id': str}, parse_dates=['t_dat'])\narticles = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/articles.csv', dtype={'article_id': str})\nprint(\"Dữ liệu đã tải xong.\")\n\n\n# === BƯỚC 2: CÁC HÀM XÂY DỰNG BẢN ĐỒ RECALL (NÂNG CẤP VỚI SCORE) ===\n\ndef build_transactional_item_map_with_jaccard(transactions_df, recent_days=30, top_k=50):\n    print(f\"\\n--- Bắt đầu xây dựng Bản đồ Giao dịch (với Jaccard Score) ---\")\n    max_date = transactions_df['t_dat'].max()\n    min_date = max_date - timedelta(days=recent_days)\n    recent_transactions = transactions_df[transactions_df['t_dat'] >= min_date]\n    \n    print(\"Đang đếm số lần mua chung...\")\n    pairs_df = recent_transactions.merge(recent_transactions, on='customer_id')\n    pair_counts = pairs_df[pairs_df['article_id_x'] != pairs_df['article_id_y']].groupby(['article_id_x', 'article_id_y']).size().reset_index(name='co_purchase_count')\n    \n    print(\"Đang đếm tổng số lần mua của mỗi sản phẩm...\")\n    total_purchases = recent_transactions['article_id'].value_counts().to_frame('total_count')\n    \n    print(\"Đang tính toán Jaccard Index...\")\n    pair_counts = pair_counts.merge(total_purchases, left_on='article_id_x', right_index=True)\n    pair_counts = pair_counts.merge(total_purchases.rename(columns={'total_count': 'total_count_y'}), left_on='article_id_y', right_index=True)\n    \n    pair_counts['jaccard_score'] = pair_counts['co_purchase_count'] / (pair_counts['total_count'] + pair_counts['total_count_y'] - pair_counts['co_purchase_count'])\n\n    item_map = defaultdict(list)\n    pair_counts = pair_counts.sort_values(['article_id_x', 'jaccard_score'], ascending=[True, False])\n\n    for source_item, group in tqdm(pair_counts.groupby('article_id_x'), desc=\"Tạo map giao dịch (Jaccard)\"):\n        top_k_recs = group.head(top_k)\n        item_map[source_item] = list(zip(top_k_recs['article_id_y'], top_k_recs['jaccard_score']))\n            \n    print(\"Xây dựng Bản đồ Giao dịch hoàn tất.\")\n    return dict(item_map)\n\ndef build_name_similarity_map_batched_with_scores(articles_df, top_k=50, batch_size=5000):\n    print(f\"\\n--- Bắt đầu xây dựng Bản đồ Tên SP (TF-IDF - có Score) ---\")\n    vectorizer = TfidfVectorizer(analyzer='char', ngram_range=(2, 5))\n    tfidf_matrix = vectorizer.fit_transform(articles_df['prod_name'].fillna(''))\n    \n    item_map = {}\n    article_ids = articles_df['article_id'].tolist()\n    num_batches = int(np.ceil(len(article_ids) / batch_size))\n\n    for i in tqdm(range(num_batches), desc=\"Xử lý các lô TF-IDF\"):\n        start_idx = i * batch_size\n        end_idx = min((i + 1) * batch_size, len(article_ids))\n        batch_tfidf = tfidf_matrix[start_idx:end_idx]\n        cosine_sim_batch = batch_tfidf * tfidf_matrix.T\n        \n        for j in range(cosine_sim_batch.shape[0]):\n            global_idx = start_idx + j\n            row = cosine_sim_batch[j].toarray().ravel()\n            top_indices = np.argpartition(row, -(top_k + 1))[-(top_k + 1):]\n            \n            # Lấy cả chỉ số và điểm số tương ứng\n            top_scores = row[top_indices]\n            \n            # Sắp xếp và lấy top K\n            sorted_order = np.argsort(top_scores)[::-1]\n            sorted_top_indices = top_indices[sorted_order]\n            sorted_top_scores = top_scores[sorted_order]\n            \n            final_recs = []\n            for idx, score in zip(sorted_top_indices, sorted_top_scores):\n                if idx != global_idx and len(final_recs) < top_k:\n                    final_recs.append((article_ids[idx], score))\n            \n            item_map[article_ids[global_idx]] = final_recs\n\n    print(\"Xây dựng Bản đồ Tương đồng Tên hoàn tất.\")\n    return item_map\n\ndef build_embedding_similarity_map_knn_batched_with_scores(articles_df, top_k=50, batch_size=10000):\n    print(\"\\n--- Bắt đầu xây dựng Bản đồ Embedding (KNN - có Score) ---\")\n    model = SentenceTransformer('all-MiniLM-L6-v2', device='cuda')\n    articles_df['full_desc'] = articles_df.apply(lambda row: ' '.join([str(row['prod_name']), str(row['product_type_name']), str(row['detail_desc'])]).replace('nan', ''), axis=1)\n    \n    print(\"Đang tạo embeddings...\")\n    embeddings = model.encode(articles_df['full_desc'].tolist(), show_progress_bar=True)\n    \n    print(\"Xây dựng mô hình NearestNeighbors...\")\n    knn = NearestNeighbors(n_neighbors=top_k + 1, metric='cosine', algorithm='brute', n_jobs=-1)\n    knn.fit(embeddings)\n    \n    item_map = {}\n    article_ids = articles_df['article_id'].tolist()\n    num_batches = int(np.ceil(len(embeddings) / batch_size))\n    \n    for i in tqdm(range(num_batches), desc=\"Xử lý các lô embedding\"):\n        start_idx = i * batch_size\n        end_idx = min((i + 1) * batch_size, len(embeddings))\n        batch_embeddings = embeddings[start_idx:end_idx]\n        \n        distances, indices = knn.kneighbors(batch_embeddings)\n        \n        for j in range(len(indices)):\n            global_idx = start_idx + j\n            \n            neighbor_indices = indices[j][1:]\n            neighbor_distances = distances[j][1:]\n            \n            # Chuyển đổi khoảng cách (distance) thành điểm tương đồng (similarity)\n            neighbor_similarities = 1 - neighbor_distances\n            \n            # Lưu tuple (article_id, score)\n            item_map[article_ids[global_idx]] = list(zip([article_ids[idx] for idx in neighbor_indices], neighbor_similarities))\n\n    print(\"Xây dựng Bản đồ Embedding (KNN-Batched) hoàn tất.\")\n    return item_map\n\n\n# =============================================================================\n# === BƯỚC 3: XÂY DỰNG VÀ LƯU CÁC BẢN ĐỒ (CÓ SCORE) RA FILE ===\n# =============================================================================\n# 3a. Giao dịch (sử dụng Jaccard)\nprint(\"Bắt đầu tác vụ 1/3: Xây dựng và lưu Bản đồ Giao dịch (với Score)...\")\ntransaction_map = build_transactional_item_map_with_jaccard(transactions)\nwith open('/kaggle/working/transaction_map_with_scores.pkl', 'wb') as f:\n    pickle.dump(transaction_map, f)\nprint(\">>> Đã lưu thành công transaction_map_with_scores.pkl\")\ndel transaction_map; gc.collect()\n\n# 3b. Tên sản phẩm (có Score)\nprint(\"\\nBắt đầu tác vụ 2/3: Xây dựng và lưu Bản đồ Tên SP (với Score)...\")\nname_sim_map = build_name_similarity_map_batched_with_scores(articles)\nwith open('/kaggle/working/name_sim_map_with_scores.pkl', 'wb') as f:\n    pickle.dump(name_sim_map, f)\nprint(\">>> Đã lưu thành công name_sim_map_with_scores.pkl\")\ndel name_sim_map; gc.collect()\n\n# 3c. Embedding (có Score)\nprint(\"\\nBắt đầu tác vụ 3/3: Xây dựng và lưu Bản đồ Embedding (với Score)...\")\nembedding_sim_map = build_embedding_similarity_map_knn_batched_with_scores(articles)\nwith open('/kaggle/working/embedding_sim_map_with_scores.pkl', 'wb') as f:\n    pickle.dump(embedding_sim_map, f)\nprint(\">>> Đã lưu thành công embedding_sim_map_with_scores.pkl\")\ndel embedding_sim_map; gc.collect()\n\nprint(\"\\n\\n*** HOÀN TẤT! 3 file .pkl (CÓ SCORE) đã được tạo. ***\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null}]}