{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.11.13","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"gpu","dataSources":[{"sourceId":31254,"databundleVersionId":3103714,"sourceType":"competition"}],"dockerImageVersionId":31155,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":true}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"!pip install -q scikit-learn sentence-transformers","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2025-11-03T08:28:22.369140Z","iopub.execute_input":"2025-11-03T08:28:22.369300Z","iopub.status.idle":"2025-11-03T08:29:41.429317Z","shell.execute_reply.started":"2025-11-03T08:28:22.369284Z","shell.execute_reply":"2025-11-03T08:29:41.428541Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# Tải các thư viện cần thiết\nimport pandas as pd\nimport numpy as np\nfrom datetime import timedelta\nfrom collections import defaultdict\nimport warnings\nwarnings.filterwarnings('ignore')\nimport sys # DEBUG: Dùng để kiểm tra bộ nhớ\n\n# Thư viện cho Content-based\nfrom sklearn.feature_extraction.text import TfidfVectorizer\nfrom sklearn.metrics.pairwise import cosine_similarity\nfrom sentence_transformers import SentenceTransformer\n\nprint(\"Các thư viện đã sẵn sàng.\")\n\n# Tải dữ liệu\ntry:\n    transactions = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/transactions_train.csv', dtype={'article_id': str}, parse_dates=['t_dat'])\n    articles = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/articles.csv', dtype={'article_id': str})\nexcept FileNotFoundError:\n    print(\"Không tìm thấy file trên Kaggle, vui lòng cập nhật đường dẫn.\")\n\nprint(\"Dữ liệu đã tải xong.\")\n# DEBUG: In ra kích thước và bộ nhớ sử dụng của các DataFrame ban đầu\nprint(f\"Transactions: {transactions.shape[0]:,} dòng, {transactions.shape[1]} cột. Memory: {transactions.memory_usage(deep=True).sum() / 1e9:.2f} GB\")\nprint(f\"Articles: {articles.shape[0]:,} dòng, {articles.shape[1]} cột. Memory: {articles.memory_usage(deep=True).sum() / 1e9:.2f} GB\")","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_transactional_item_map(transactions_df, recent_days=30, top_k=50):\n    print(f\"\\n--- Bắt đầu xây dựng Bản đồ Giao dịch (dữ liệu {recent_days} ngày cuối) ---\")\n    \n    # 1. Lọc dữ liệu gần đây\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    # DEBUG: Hiển thị số lượng giao dịch được sử dụng\n    print(f\"# DEBUG: Đang làm việc trên {len(recent_transactions):,} giao dịch từ {min_date.date()} đến {max_date.date()}.\")\n    \n    # 2. Tạo các cặp sản phẩm\n    df = recent_transactions[['customer_id', 'article_id']]\n    merged_df = pd.merge(df, df, on='customer_id', suffixes=('_left', '_right'))\n    # DEBUG: Hiển thị kích thước của DataFrame sau khi tự join (bước tốn tài nguyên nhất)\n    print(f\"# DEBUG: DataFrame sau khi self-join có {len(merged_df):,} dòng.\")\n    \n    pairs = merged_df[merged_df['article_id_left'] != merged_df['article_id_right']]\n    # DEBUG: Hiển thị số lượng cặp sản phẩm hợp lệ\n    print(f\"# DEBUG: Tìm thấy {len(pairs):,} cặp sản phẩm (đã loại bỏ cặp trùng).\")\n    \n    # 3. Đếm tần suất\n    pair_counts = pairs.groupby(['article_id_left', 'article_id_right']).size().reset_index(name='count')\n    # DEBUG: Hiển thị số lượng cặp sản phẩm duy nhất\n    print(f\"# DEBUG: Có {len(pair_counts):,} cặp sản phẩm duy nhất được tìm thấy.\")\n    \n    # 4. Xây dựng bản đồ\n    item_map = defaultdict(list)\n    pair_counts_sorted = pair_counts.sort_values('count', ascending=False)\n    \n    print(\"# DEBUG: Bắt đầu lặp để tạo dictionary tra cứu...\")\n    for row in pair_counts_sorted.itertuples(index=False):\n        source_item = row.article_id_left\n        related_item = row.article_id_right\n        if len(item_map[source_item]) < top_k:\n            item_map[source_item].append(related_item)\n            \n    # DEBUG: Hiển thị kích thước của bản đồ cuối cùng\n    print(f\"# DEBUG: Bản đồ giao dịch hoàn thành với {len(item_map)} sản phẩm gốc có gợi ý.\")\n    print(\"Xây dựng Bản đồ Giao dịch hoàn tất.\")\n    return dict(item_map)\n\n# Chạy hàm với debug\ntransaction_map = build_transactional_item_map(transactions, recent_days=30, top_k=50)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_name_similarity_map(articles_df, top_k=50):\n    \"\"\"\n    Xây dựng bản đồ item-to-item dựa trên sự tương đồng về tên sản phẩm (TF-IDF).\n    *** PHIÊN BẢN TỐI ƯU BỘ NHỚ ***\n    \"\"\"\n    print(\"\\n--- Bắt đầu xây dựng Bản đồ Tương đồng Tên (TF-IDF) - Tối ưu hóa ---\")\n    \n    # TfidfVectorizer mặc định đã chuẩn hóa L2, nên tích ma trận sẽ tương đương cosine similarity\n    vectorizer = TfidfVectorizer(analyzer='char', ngram_range=(2, 5))\n    tfidf_matrix = vectorizer.fit_transform(articles_df['prod_name'].fillna(''))\n    print(f\"# DEBUG: Ma trận TF-IDF thưa có kích thước: {tfidf_matrix.shape}.\")\n\n    print(\"# DEBUG: Đang tính toán ma trận tương đồng (sparse matrix multiplication)...\")\n    # TỐI ƯU: Thay vì dùng cosine_similarity, ta nhân ma trận thưa với chuyển vị của nó.\n    # Đây là cách hiệu quả hơn nhiều về bộ nhớ.\n    cosine_sim_sparse = tfidf_matrix * tfidf_matrix.T\n    print(f\"# DEBUG: Ma trận tương đồng thưa có kích thước: {cosine_sim_sparse.shape}.\")\n    \n    item_map = {}\n    article_ids = articles_df['article_id'].tolist()\n    \n    print(\"# DEBUG: Bắt đầu lặp để tạo dictionary tra cứu...\")\n    for idx, article_id in enumerate(article_ids):\n        if (idx + 1) % 10000 == 0:\n            print(f\"# DEBUG: Đã xử lý {idx + 1}/{len(article_ids)} sản phẩm...\")\n            \n        # Lấy một hàng của ma trận thưa\n        row = cosine_sim_sparse[idx]\n        \n        # Chuyển hàng đó thành mảng đặc để sắp xếp\n        # .A1 là cách nhanh để chuyển một hàng/cột của ma trận thưa thành mảng numpy 1D\n        sim_scores_array = row.toarray().ravel()\n        \n        # Lấy chỉ số của các phần tử lớn nhất, hiệu quả hơn so với việc sort toàn bộ\n        # np.argpartition sẽ tìm top k phần tử lớn nhất mà không cần sort toàn bộ\n        top_indices = np.argpartition(sim_scores_array, -(top_k + 1))[-(top_k + 1):]\n        \n        # Sắp xếp lại chỉ top k+1 phần tử này\n        sorted_top_indices = top_indices[np.argsort(sim_scores_array[top_indices])][::-1]\n        \n        # Bỏ qua phần tử đầu tiên (là chính nó)\n        final_top_indices = [i for i in sorted_top_indices if i != idx][:top_k]\n\n        top_articles = [article_ids[i] for i in final_top_indices]\n        item_map[article_id] = top_articles\n        \n    print(f\"# DEBUG: Bản đồ tương đồng tên hoàn thành với {len(item_map)} sản phẩm gốc có gợi ý.\")\n    print(\"Xây dựng Bản đồ Tương đồng Tên hoàn tất.\")\n    return item_map\n\n\nprint(\"Tạo một mẫu articles nhỏ để kiểm tra nhanh pipeline...\")\narticles_sample_for_tfidf = articles.head(40000) # Chỉ lấy 20k sản phẩm đầu tiên\n\n# Gọi hàm trên DỮ LIỆU MẪU\nname_sim_map = build_name_similarity_map(articles_sample_for_tfidf, top_k=50)\n\n# (Tùy chọn) In ra một ví dụ để kiểm tra\nsample_article_id_name = '0706016001'\nif sample_article_id_name in name_sim_map:\n    print(f\"\\nKiểm tra: Sản phẩm có tên tương tự '{sample_article_id_name}':\")\n    similar_ids = name_sim_map[sample_article_id_name][:5]\n    print(articles[articles['article_id'].isin(similar_ids)][['article_id', 'prod_name']])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def build_embedding_similarity_map(articles_df, top_k=50):\n    print(\"\\n--- Bắt đầu xây dựng Bản đồ Tương đồng Embedding ---\")\n    \n    model = SentenceTransformer('all-MiniLM-L6-v2')\n    \n    articles_df['full_desc'] = articles_df.apply(\n        lambda row: ' '.join([\n            str(row['prod_name']), str(row['product_type_name']), \n            str(row['product_group_name']), str(row['detail_desc'])\n        ]).replace('nan', ''), axis=1)\n    # DEBUG: In ra một ví dụ về văn bản đầy đủ\n    print(f\"# DEBUG: Ví dụ về văn bản đầy đủ để tạo embedding:\\n'{articles_df['full_desc'].iloc[0]}'\")\n\n    print(\"Đang tạo embeddings... (Thanh tiến trình sẽ hiển thị bên dưới)\")\n    embeddings = model.encode(articles_df['full_desc'].tolist(), show_progress_bar=True, device='cuda')\n    # DEBUG: Hiển thị kích thước của mảng embeddings\n    print(f\"# DEBUG: Mảng embedding có kích thước: {embeddings.shape} (sản phẩm, chiều embedding).\")\n    \n    print(\"# DEBUG: Đang tính toán ma trận tương đồng cosine...\")\n    cosine_sim = cosine_similarity(embeddings, embeddings)\n    # DEBUG: Hiển thị kích thước và bộ nhớ của ma trận tương đồng\n    print(f\"# DEBUG: Ma trận tương đồng có kích thước: {cosine_sim.shape}, Bộ nhớ: {sys.getsizeof(cosine_sim) / 1e9:.2f} GB.\")\n    \n    item_map = {}\n    article_ids = articles_df['article_id'].tolist()\n    \n    print(\"# DEBUG: Bắt đầu lặp để tạo dictionary tra cứu...\")\n    for idx, article_id in enumerate(article_ids):\n        # DEBUG: Thêm một bộ đếm tiến độ\n        if (idx + 1) % 5000 == 0:\n            print(f\"# DEBUG: Đã xử lý {idx + 1}/{len(article_ids)} sản phẩm...\")\n            \n        sim_scores = list(enumerate(cosine_sim[idx]))\n        sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)\n        top_indices = [i[0] for i in sim_scores[1:top_k+1]]\n        top_articles = [article_ids[i] for i in top_indices]\n        item_map[article_id] = top_articles\n        \n    print(f\"# DEBUG: Bản đồ tương đồng embedding hoàn thành với {len(item_map)} sản phẩm gốc có gợi ý.\")\n    print(\"Xây dựng Bản đồ Tương đồng Embedding hoàn tất.\")\n    return item_map\n\n# Chạy với debug trên một mẫu nhỏ\narticles_sample_for_embedding = articles.head(40000)\nembedding_sim_map = build_embedding_similarity_map(articles_sample_for_embedding, top_k=50)","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"def generate_combined_candidates_for_user(customer_history, trans_map, name_map, embed_map, num_recent_items=5):\n    # 1. Lấy các sản phẩm mua gần đây\n    recent_items = customer_history.sort_values('t_dat', ascending=False)['article_id'].unique()[:num_recent_items]\n    \n    if len(recent_items) == 0:\n        print(\"# DEBUG: Khách hàng này không có lịch sử mua hàng.\")\n        return []\n    \n    # DEBUG: In ra các sản phẩm \"hạt giống\"\n    print(f\"\\n# DEBUG: Tìm ứng viên dựa trên {len(recent_items)} sản phẩm mua gần nhất: {recent_items}\")\n    \n    candidate_set = set()\n    \n    for item_id in recent_items:\n        # DEBUG: Theo dõi số lượng ứng viên từ mỗi nguồn\n        trans_cands = trans_map.get(item_id, [])\n        name_cands = name_map.get(item_id, [])\n        embed_cands = embed_map.get(item_id, [])\n        \n        candidate_set.update(trans_cands)\n        candidate_set.update(name_cands)\n        candidate_set.update(embed_cands)\n        \n        print(f\"# DEBUG: Từ sản phẩm '{item_id}': Tìm thấy {len(trans_cands)} (giao dịch), {len(name_cands)} (tên), {len(embed_cands)} (embedding) ứng viên.\")\n    \n    # DEBUG: Hiển thị số lượng ứng viên trước và sau khi lọc\n    initial_candidate_count = len(candidate_set)\n    \n    # 3. Loại bỏ các sản phẩm khách hàng đã mua\n    purchased_items = set(customer_history['article_id'].unique())\n    final_candidates = list(candidate_set - purchased_items)\n    \n    print(f\"# DEBUG: Tổng số ứng viên ban đầu: {initial_candidate_count}\")\n    print(f\"# DEBUG: Số ứng viên sau khi loại bỏ {len(purchased_items)} sản phẩm đã mua: {len(final_candidates)}\")\n    \n    return final_candidates\n\n# Ví dụ chạy với debug\nsample_customer_id = transactions['customer_id'].unique()[200]\ncustomer_purchase_history = transactions[transactions['customer_id'] == sample_customer_id]\n\ncandidates = generate_combined_candidates_for_user(\n    customer_purchase_history,\n    transaction_map,\n    name_sim_map,\n    embedding_sim_map\n)\n\nprint(f\"\\n---> KẾT QUẢ CUỐI CÙNG: Đã tạo ra {len(candidates)} ứng viên duy nhất cho khách hàng {sample_customer_id[:10]}...\")\nprint(\"Một vài ví dụ:\", candidates[:15])","metadata":{"trusted":true},"outputs":[],"execution_count":null},{"cell_type":"code","source":"# =============================================================================\n# === BƯỚC 0: CÀI ĐẶT THƯ VIỆN ===\n# =============================================================================\n!pip install -q sentence-transformers tqdm\n\n# =============================================================================\n# === BƯỚC 1: TẢI THƯ VIỆN VÀ DỮ LIỆU ===\n# =============================================================================\nimport pandas as pd\nimport numpy as np\nfrom datetime import timedelta\nfrom collections import defaultdict\nimport warnings\nfrom tqdm.auto import tqdm\n\nwarnings.filterwarnings('ignore')\n\nfrom sklearn.feature_extraction.text import TfidfVectorizer\nfrom sentence_transformers import SentenceTransformer\nfrom sklearn.metrics.pairwise import cosine_similarity\n\nprint(\"Các thư viện đã sẵn sàng.\")\n\n# Tải dữ liệu từ đường dẫn Kaggle\ntry:\n    transactions = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/transactions_train.csv', dtype={'article_id': str}, parse_dates=['t_dat'])\n    articles = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/articles.csv', dtype={'article_id': str})\n    customers = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/customers.csv')\nexcept FileNotFoundError:\n    print(\"Không tìm thấy file trên Kaggle. Vui lòng kiểm tra lại đường dẫn.\")\n    # Tạo dữ liệu giả để code chạy không lỗi nếu không tìm thấy file\n    transactions = pd.DataFrame()\n    articles = pd.DataFrame()\n    customers = pd.DataFrame()\n\n\nprint(\"Dữ liệu đã tải xong.\")\nprint(f\"Transactions: {transactions.shape[0]:,} dòng\")\nprint(f\"Articles: {articles.shape[0]:,} dòng\")\nprint(f\"Customers: {customers.shape[0]:,} dòng\")\n\n\n# =============================================================================\n# === BƯỚC 2: CÁC HÀM XÂY DỰNG BẢN ĐỒ RECALL ===\n# =============================================================================\n\ndef build_transactional_item_map(transactions_df, recent_days=30, top_k=50):\n    print(f\"\\n--- Bắt đầu xây dựng Bản đồ Giao dịch (dữ liệu {recent_days} ngày cuối) ---\")\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    df = recent_transactions[['customer_id', 'article_id']]\n    merged_df = pd.merge(df, df, on='customer_id', suffixes=('_left', '_right'))\n    pairs = merged_df[merged_df['article_id_left'] != merged_df['article_id_right']]\n    pair_counts = pairs.groupby(['article_id_left', 'article_id_right']).size().reset_index(name='count')\n    item_map = defaultdict(list)\n    pair_counts_sorted = pair_counts.sort_values('count', ascending=False)\n    for row in tqdm(pair_counts_sorted.itertuples(index=False), total=len(pair_counts_sorted), desc=\"Tạo map giao dịch\"):\n        source_item, related_item = row.article_id_left, row.article_id_right\n        if len(item_map[source_item]) < top_k:\n            item_map[source_item].append(related_item)\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(articles_df, top_k=50):\n    print(\"\\n--- Bắt đầu xây dựng Bản đồ Tương đồng Tên (TF-IDF) ---\")\n    vectorizer = TfidfVectorizer(analyzer='char', ngram_range=(2, 5))\n    tfidf_matrix = vectorizer.fit_transform(articles_df['prod_name'].fillna(''))\n    cosine_sim_sparse = tfidf_matrix * tfidf_matrix.T\n    item_map = {}\n    article_ids = articles_df['article_id'].tolist()\n    for idx in tqdm(range(len(article_ids)), desc=\"Tạo map tên SP\"):\n        row = cosine_sim_sparse[idx].toarray().ravel()\n        top_indices = np.argpartition(row, -(top_k + 1))[-(top_k + 1):]\n        sorted_top_indices = top_indices[np.argsort(row[top_indices])][::-1]\n        final_top_indices = [i for i in sorted_top_indices if i != idx][:top_k]\n        item_map[article_ids[idx]] = [article_ids[i] for i in final_top_indices]\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(articles_df, top_k=50):\n    print(\"\\n--- Bắt đầu xây dựng Bản đồ Tương đồng Embedding ---\")\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['product_group_name']), str(row['detail_desc'])]).replace('nan', ''), axis=1)\n    embeddings = model.encode(articles_df['full_desc'].tolist(), show_progress_bar=True)\n    cosine_sim = cosine_similarity(embeddings, embeddings)\n    item_map = {}\n    article_ids = articles_df['article_id'].tolist()\n    for idx in tqdm(range(len(article_ids)), desc=\"Tạo map embedding\"):\n        sim_scores = list(enumerate(cosine_sim[idx]))\n        sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)\n        top_indices = [i[0] for i in sim_scores[1:top_k+1]]\n        item_map[article_ids[idx]] = [article_ids[i] for i in top_indices]\n    print(\"Xây dựng Bản đồ Tương đồng Embedding hoàn tất.\")\n    return item_map\n\n# =============================================================================\n# === BƯỚC 3: XÂY DỰNG CÁC BẢN ĐỒ RECALL THỰC TẾ ===\n# =============================================================================\n\n# Chạy trên toàn bộ dữ liệu giao dịch\ntransaction_map = build_transactional_item_map(transactions)\n\n# Chạy trên mẫu lớn hơn để có kết quả tốt hơn, nhưng vẫn đảm bảo thời gian chạy\n# Bạn có thể tăng/giảm con số này tùy vào cấu hình máy\nSAMPLE_SIZE = 40000 \narticles_sample = articles.head(SAMPLE_SIZE)\n\nname_sim_map = build_name_similarity_map(articles_sample)\nembedding_sim_map = build_embedding_similarity_map(articles_sample)\n\n\n# =============================================================================\n# === BƯỚC 4: HÀM TẠO FILE SUBMISSION ===\n# =============================================================================\n\ndef generate_recall_file(target_customers_df, transactions_df, recall_maps: list, output_filename: str, num_recent_items=5):\n    print(f\"\\n--- Bắt đầu tạo file '{output_filename}' ---\")\n    \n    all_predictions = []\n    customer_ids = target_customers_df['customer_id'].unique()\n    \n    # Tạo map lịch sử mua hàng để tra cứu nhanh\n    customer_history_map = transactions_df.groupby('customer_id')['article_id'].apply(list).to_dict()\n    # Tạo map lịch sử mua hàng gần đây để tra cứu nhanh\n    recent_transactions_df = transactions_df[transactions_df['t_dat'] >= (transactions_df['t_dat'].max() - timedelta(days=30))]\n    customer_recent_history_map = recent_transactions_df.groupby('customer_id')['article_id'].apply(lambda x: list(dict.fromkeys(x))).to_dict()\n\n    for customer_id in tqdm(customer_ids, desc=f\"Tạo file {output_filename}\"):\n        \n        purchased_items = set(customer_history_map.get(customer_id, []))\n        recent_items = customer_recent_history_map.get(customer_id, [])[-num_recent_items:] # Lấy N item cuối cùng\n        \n        candidate_set = set()\n        \n        if len(recent_items) > 0:\n            for item_id in recent_items:\n                for recall_map in recall_maps:\n                    candidate_set.update(recall_map.get(item_id, []))\n        \n        final_candidates = list(candidate_set - purchased_items)\n        top_12_preds = final_candidates[:12]\n        \n        # Thêm các sản phẩm phổ biến nhất để lấp đầy nếu không đủ 12\n        if len(top_12_preds) < 12:\n            # (Tùy chọn) Có thể thêm logic lấp đầy ở đây, ví dụ: các sản phẩm phổ biến nhất\n            pass\n\n        prediction_str = ' '.join(top_12_preds)\n        \n        all_predictions.append({'customer_id': customer_id, 'prediction': prediction_str})\n        \n    submission_df = pd.DataFrame(all_predictions)\n    # Lưu file vào thư mục output của Kaggle\n    submission_df.to_csv(f\"/kaggle/working/{output_filename}\", index=False)\n    \n    print(f\"Đã tạo thành công file '/kaggle/working/{output_filename}'.\")\n    print(\"Xem trước 5 dòng đầu:\")\n    print(submission_df.head())\n\n\n# =============================================================================\n# === BƯỚC 5: TẠO 2 FILE RECALL RIÊNG BIỆT ===\n# =============================================================================\n\n# Lấy danh sách tất cả khách hàng cần dự đoán từ file sample_submission\ntry:\n    sample_submission = pd.read_csv('/kaggle/input/h-and-m-personalized-fashion-recommendations/sample_submission.csv')\n    target_customers = sample_submission\nexcept FileNotFoundError:\n    print(\"Không tìm thấy sample_submission.csv, sẽ dùng toàn bộ khách hàng trong customers.csv\")\n    target_customers = customers\n\n\n# --- TẠO FILE 1: CHỈ DỰA TRÊN GIAO DỊCH ---\nrecall_maps_transactional = [transaction_map]\ngenerate_recall_file(\n    target_customers,\n    transactions,\n    recall_maps_transactional,\n    \"transactional_recall_submission.csv\"\n)\n\n# --- TẠO FILE 2: CHỈ DỰA TRÊN NỘI DUNG ---\nrecall_maps_content = [name_sim_map, embedding_sim_map]\ngenerate_recall_file(\n    target_customers,\n    transactions,\n    recall_maps_content,\n    \"content_based_recall_submission.csv\"\n)\n\nprint(\"\\nHoàn tất! Đã tạo ra 2 file recall trong thư mục /kaggle/working/.\")","metadata":{"trusted":true},"outputs":[],"execution_count":null}]}