{"metadata":{"kernelspec":{"language":"python","display_name":"Python 3","name":"python3"},"language_info":{"name":"python","version":"3.12.12","mimetype":"text/x-python","codemirror_mode":{"name":"ipython","version":3},"pygments_lexer":"ipython3","nbconvert_exporter":"python","file_extension":".py"},"kaggle":{"accelerator":"none","dataSources":[{"sourceId":31254,"databundleVersionId":3103714,"sourceType":"competition"},{"sourceId":14241442,"sourceType":"datasetVersion","datasetId":9085906},{"sourceId":14411790,"sourceType":"datasetVersion","datasetId":9204519},{"sourceId":14437470,"sourceType":"datasetVersion","datasetId":9221939}],"dockerImageVersionId":31234,"isInternetEnabled":true,"language":"python","sourceType":"notebook","isGpuEnabled":false}},"nbformat_minor":4,"nbformat":4,"cells":[{"cell_type":"code","source":"from gensim.models import Word2Vec\nimport pickle\nimport pandas as pd\nimport pickle\nimport os\nimport gc\n\ntrans = pd.read_csv(\"/kaggle/input/h-and-m-personalized-fashion-recommendations/transactions_train.csv\")\nuid2idx = pickle.load(open('/kaggle/input/mapping/index_id_map/user_id2index.pkl', \"rb\"))\niid2idx = pickle.load(open('/kaggle/input/mapping/index_id_map/item_id2index.pkl', \"rb\"))\n# đảm bảo đúng thứ tự thời gian\ntrans['customer_id'] = trans['customer_id'].map(uid2idx)\ntrans['article_id'] = trans['article_id'].map(iid2idx)\n\n\ntrans[\"t_dat\"] = pd.to_datetime(trans[\"t_dat\"])\n\ntrans = trans.sort_values([\"customer_id\", \"t_dat\"])\ntrans['week'] = (pd.to_datetime('2020-09-29') - pd.to_datetime(trans['t_dat'])).dt.days // 7\n","metadata":{"_uuid":"8f2839f25d086af736a60e9eeb907d3b93b6e0e5","_cell_guid":"b1076dfc-b9ad-4769-8c92-a6c4dae69d19","trusted":true,"execution":{"iopub.status.busy":"2026-01-09T11:56:01.126597Z","iopub.execute_input":"2026-01-09T11:56:01.128124Z","iopub.status.idle":"2026-01-09T11:57:10.228289Z","shell.execute_reply.started":"2026-01-09T11:56:01.127999Z","shell.execute_reply":"2026-01-09T11:57:10.227079Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"params = {\n    'topK_per_item': [20],\n    'topK' : [100]\n}\nwindow_week = 4\nwindow_size = 5\npath = 'Item2Vec'\nname = 'Item2Vec'\nos.makedirs('model', exist_ok=True)\nos.makedirs('dict_neighbor', exist_ok=True)\nos.makedirs(path, exist_ok=True)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T12:00:24.55005Z","iopub.execute_input":"2026-01-09T12:00:24.551108Z","iopub.status.idle":"2026-01-09T12:00:24.557946Z","shell.execute_reply.started":"2026-01-09T12:00:24.551064Z","shell.execute_reply":"2026-01-09T12:00:24.556959Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"sentences = (\n    trans[trans['week']<= window_size]\n    .groupby(\"customer_id\")[\"article_id\"]\n    .apply(list)\n    .tolist()\n)\n\nitem2vec = Word2Vec(\n    sentences=sentences,\n    vector_size=64,     # 32–128 đều ổn\n    window=window_size,           # 3–7\n    min_count=5,        # bỏ item quá hiếm\n    workers=8,\n    sg=1,               # skip-gram (BẮT BUỘC)\n    negative=10,\n    epochs=10\n)\n\nitem2vec.save(f\"model/week{0}_item2vec.model\")\n\nTOPK = 100\nitem_neighbors = {}\n\nfor item in item2vec.wv.index_to_key:\n    sims = item2vec.wv.most_similar(item, topn=TOPK)\n    item_neighbors[item] = [(i, s) for i, s in sims]\n\n\nwith open(f\"dict_neighbor/week{0}_neighbors.pkl\", \"wb\") as f:\n    pickle.dump(item_neighbors, f)","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T12:00:24.560178Z","iopub.execute_input":"2026-01-09T12:00:24.560621Z","iopub.status.idle":"2026-01-09T12:01:27.507714Z","shell.execute_reply.started":"2026-01-09T12:00:24.560593Z","shell.execute_reply":"2026-01-09T12:01:27.506216Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"topK = params['topK'][0]\ntopK_per_item = params['topK_per_item'][0]\nprint(f'RULE: {name}_{window_week}_{window_size}_{topK_per_item}_{topK}')\nprint(f'Week: {0}')\nwith open(\n    f\"dict_neighbor/week{0}_neighbors.pkl\",\n    \"rb\"\n) as f:\n    item_neighbors = pickle.load(f)\n\nuser_hist = (\n    trans[trans[\"week\"] <= 2]\n    .sort_values(['customer_id', \"t_dat\"], ascending=[True, False])[['customer_id', 'article_id']]\n)\n\nuser_hist = user_hist.rename(columns={'article_id':'seed_item'})\nrows = []\n\nfor item, neis in item_neighbors.items():\n    for rank, (nei, sim) in enumerate(neis[:topK_per_item]):   # 👈 cắt sớm\n        rows.append({\n            \"seed_item\": item,\n            \"article_id\": nei,\n            \"score\": sim,\n        })\n\nitem_neighbors_df = pd.DataFrame(rows)\n\ndel item_neighbors\ngc.collect()\n\ndf_cand = user_hist.merge(item_neighbors_df, on='seed_item', how='left')\n\ncandidates_df = (\n    df_cand\n    .groupby(['customer_id', 'article_id'], as_index=False)['score']\n    .sum()\n    .sort_values(['customer_id', 'score'], ascending=[True, False])\n)\ncandidates_df['rank'] = candidates_df.groupby('customer_id').cumcount()\n\ncandidates_df = candidates_df[candidates_df['rank'] <= topK].drop(columns=['rank'])\n\nlast_item_map = (\n    trans[~trans[\"customer_id\"].isin(candidates_df[\"customer_id\"].unique())]\n    .sort_values([\"customer_id\", \"t_dat\"], ascending=[True, False])\n    .groupby(\"customer_id\")[\"article_id\"]\n    .first()\n    .reset_index().rename(columns={'article_id':'seed_item'})\n)\n\nuser_not_in_window = last_item_map.merge(item_neighbors_df, on=['seed_item'], how='left').drop(columns='seed_item')\n\ndf_cand = pd.concat([candidates_df, user_not_in_window]).rename(columns={'score':name})\n\ndf_cand.to_parquet(f'{path}/week{0}_candidate.pqt')\n\n","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T12:05:01.319237Z","iopub.execute_input":"2026-01-09T12:05:01.320564Z","iopub.status.idle":"2026-01-09T12:05:25.25206Z","shell.execute_reply.started":"2026-01-09T12:05:01.320523Z","shell.execute_reply":"2026-01-09T12:05:25.250938Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"df_cand","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T12:05:25.254267Z","iopub.execute_input":"2026-01-09T12:05:25.254688Z","iopub.status.idle":"2026-01-09T12:05:25.28335Z","shell.execute_reply.started":"2026-01-09T12:05:25.254659Z","shell.execute_reply":"2026-01-09T12:05:25.282394Z"}},"outputs":[],"execution_count":null},{"cell_type":"code","source":"!zip Item2Vec.zip -r Item2Vec","metadata":{"trusted":true,"execution":{"iopub.status.busy":"2026-01-09T12:34:23.745862Z","iopub.execute_input":"2026-01-09T12:34:23.746291Z","iopub.status.idle":"2026-01-09T12:34:45.706373Z","shell.execute_reply.started":"2026-01-09T12:34:23.746258Z","shell.execute_reply":"2026-01-09T12:34:45.705173Z"}},"outputs":[],"execution_count":null}]}