-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfashion_product_recommendation.py
463 lines (320 loc) · 18.3 KB
/
fashion_product_recommendation.py
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
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
# -*- coding: utf-8 -*-
"""Fashion Product Recommendation.ipynb
Automatically generated by Colab.
Original file is located at
https://colab.research.google.com/drive/1raSYIwR372l3V_f1hzAPrWt24kh2Vv6y
# Fashion Product Recommendation System
## 1. Import Library
"""
# Commented out IPython magic to ensure Python compatibility.
import pandas as pd
import numpy as np
import matplotlib.pyplot as plt
import seaborn as sns
import os
import zipfile
import math
# %matplotlib inline
import tensorflow as tf
from tensorflow.keras import layers, models
from sklearn.preprocessing import LabelEncoder
from sklearn.model_selection import train_test_split
from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.metrics.pairwise import cosine_similarity
import os
import warnings
warnings.filterwarnings('ignore')
import os
import warnings
warnings.filterwarnings('ignore')
"""## 2. Loading Data"""
!kaggle datasets download -d bhanupratapbiswas/fashion-products
with zipfile.ZipFile("fashion-products.zip", 'r') as zip_ref:
zip_ref.extractall("fashion_data")
os.listdir("fashion_data")
product_data = pd.read_csv("/content/fashion_data/fashion_products.csv")
print('Shape of Job Skills dataset: ',product_data.shape)
product_data
"""Output kode di atas memberikan informasi sebagai berikut:
- Dataset berjumlah 1000 baris (records)
- Terdapat 9 kolom yaitu User ID, Product ID, Product Name, Brand, Category, Price, Rating, Color, Size.
## 3. Exploratory Data Analysis
### Mengupas Informasi Dataset
"""
product_data.info()
"""Berdasarkan informasi di atas, dataset memiliki beberapa kriteria sebagai berikut:
- 1 Kolom dengan tipe float64 yaitu Rating
- 3 Kolom dengan tipe int64 yaitu User ID, Product ID, dan Price
- 5 Kolom dengan tipe object yaitu Product Name, Brand, Category, Color, dan Size
"""
product_data.describe()
"""Fungsi describe() di atas memberi informasi statistik pada masing-masing kolom di kedua dataset, antara lain:
- Count adalah jumlah sampel pada data
- Mean adalah nilai rata-rata
- Std adalah standar deviasi
- Min yaitu nilai minimum setiap kolom
- 25% adalah kuartil pertama, Kuartil adalah nilai yang menandai batas interval dalam empat bagian sebaran yang sama.
- 50% adalah kuartil kedua, atau biasa juga disebut median
- 75% adalah kuartil ketiga
- Max adalah nilai maksimum
"""
product_data.isnull().sum()
"""Berdasarkan kode di atas, kedua dataset tidak memiliki missing value.
### Distribusi Produk per Kategori
"""
plt.figure(figsize=(8, 6))
sns.countplot(data=product_data, x='Category', palette='Set2')
plt.title('Distribusi Produk per Kategori')
plt.show()
"""Dari visualisasi di atas nampak beberapa informasi di antaranya:
- Secara keseluruhan ketiga kategori produk memiliki jumlah yang mirip.
- Kategori Kid's Fashion memiliki jumlah yang paling unggul disusul oleh Women's Fashion kemudian Men's Fashion.
### Distribusi Produk berdasarkan Brand
"""
plt.figure(figsize=(8, 6))
sns.countplot(data=product_data, x='Brand', palette='Set2')
plt.title('Distribusi Produk berdasarkan Brand')
plt.xlabel('Brand')
plt.ylabel('Jumlah')
plt.xticks(rotation=45)
plt.show()
"""Dari visualisasi di atas nampak beberapa informasi di antaranya:
- Secara keseluruhan kelima brand memiliki jumlah produk yang tidak terlalu berbeda jauh.
- Brand Nike memiliki jumlah produk yang paling unggul dibandingkan brand yang lain.
### Distribusi Brand dengan Rating Tinggi (4 ke Atas) dan Rating Rendah (2 ke Bawah)
"""
high_rated_products = product_data[product_data['Rating'] >= 4]
high_brand_counts = high_rated_products['Brand'].value_counts()
low_rated_products = product_data[product_data['Rating'] <= 2]
low_brand_counts = low_rated_products['Brand'].value_counts()
fig, axes = plt.subplots(1, 2, figsize=(16, 8))
axes[0].pie(high_brand_counts, labels=high_brand_counts.index, autopct='%1.1f%%', startangle=90)
axes[0].set_title('Distribusi Brand dengan Rating Tinggi (≥4)')
axes[0].axis('equal')
axes[1].pie(low_brand_counts, labels=low_brand_counts.index, autopct='%1.1f%%', startangle=90)
axes[1].set_title('Distribusi Brand dengan Rating Rendah (≤2)')
axes[1].axis('equal')
plt.tight_layout()
plt.show()
"""Dari visualisasi di atas didapatkan beberapa informasi antara lain:
- Gucci unggul dalam produk dengan rating yang tinggi dan memiliki jumlah produk dengan rating rendah paling sedikit.
- Sebaliknya, Adidas justru memiliki rating yang rendah terbanyak dan memiliki paling sedikit produk dengan rating tinggi.
- Zara memiliki distribusi yang seimbang antara jumlah produk dengan rating tinggi dan rating yang rendah.
### Korelasi antara Harga dan Rating
"""
correlation_matrix = product_data[['Price', 'Rating']].corr()
print(correlation_matrix)
plt.figure(figsize=(8, 6))
sns.heatmap(correlation_matrix, annot=True, cmap='coolwarm', fmt=".2f")
plt.title('Matrix Korelasi: Price vs. Rating')
plt.show()
"""Dari visualisasi di atas didapatkan informasi yaitu:
<br>
Korelasi di antara harga dan rating sebesar 0.0339 menunjukkan hubungan yang sangat lemah dan hampir tidak ada antara harga dan rating produk. Artinya, perubahasan harga tidak banyak berpengaruh pada penilaian pengguna.
## 4. Data Preparation
### Preparation untuk Content Based Filtering
#### Menggabungkan Kolom Kategorikal
Proses ini menggabungkan informasi dari beberapa kolom seperti Product Name, Brand, Category, Color, dan Size ke dalam kolom baru bernama description untuk menyediakan representasi teks terpadu dari setiap produk. Data kosong diisi dengan string kosong ('') untuk menghindari error, dan produk dengan deskripsi kosong sepenuhnya dihapus. Tujuannya adalah memastikan setiap produk memiliki deskripsi lengkap untuk analisis lebih lanjut.
"""
product_data['description'] = (
product_data['Product Name'].fillna('') + " " +
product_data['Brand'].fillna('') + " " +
product_data['Category'].fillna('') + " " +
product_data['Color'].fillna('') + " " +
product_data['Size'].fillna('')
)
product_data = product_data[product_data['description'].str.strip() != '']
product_data
"""#### Representasi Teks Description dengan TF-IDF"""
tfidf = TfidfVectorizer(stop_words='english')
tfidf_matrix = tfidf.fit_transform(product_data['description'])
print("TF-IDF Matrix Shape:", tfidf_matrix.shape)
"""### Preparation for Collaborative Filtering
#### Encoding User ID dan Product ID
"""
user_encoder = LabelEncoder()
product_encoder = LabelEncoder()
product_data['user_encoded'] = user_encoder.fit_transform(product_data['User ID'])
product_data['product_encoded'] = product_encoder.fit_transform(product_data['Product ID'])
print("Jumlah User:", len(user_encoder.classes_))
print("Jumlah Produk:", len(product_encoder.classes_))
"""#### Interaction Matrix"""
interaction_matrix = product_data.pivot(index='user_encoded', columns='product_encoded', values='Rating').fillna(0)
sparsity = 1 - np.count_nonzero(interaction_matrix) / float(interaction_matrix.size)
print(f"Data sparsity: {sparsity:.4f}")
"""#### Split Dataset"""
X = product_data[['user_encoded', 'product_encoded']].values
y = product_data['Rating'].values
X_train, X_test, y_train, y_test = train_test_split(X, y, test_size=0.2, random_state=42)
print(f"Data Train: {X_train.shape}, Data Test: {X_test.shape}")
"""## 5. Modeling with Content Based Filtering
Content Based Filtering adalah metode rekomendasi yang menggunakan informasi atau atribut dari item (misalnya, deskripsi, kategori, atau fitur lainnya) untuk mencocokkan preferensi pengguna. Sistem ini menganalisis fitur item yang sudah disukai pengguna untuk merekomendasikan item serupa.
### Cosine Similarity
"""
cosine_sim = cosine_similarity(tfidf_matrix, tfidf_matrix)
print("Cosine Similarity Matrix Shape:", cosine_sim.shape)
"""### Mendapatkan Rekomendasi Produk Fashion"""
def get_product_details(product_id, df):
product_id = int(product_id)
idx = df[df['Product ID'] == product_id].index
if idx.empty:
raise ValueError(f"Product ID {product_id} tidak ditemukan dalam dataset.")
input_product = df.iloc[[idx[0]]][['Product ID', 'Product Name', 'Brand', 'Category', 'Color', 'Size']]
return input_product
def recommend_products(product_id, df, similarity_matrix, top_n=5):
product_id = int(product_id)
product_idx = df[df['Product ID'] == product_id].index
if product_idx.empty:
raise ValueError(f"Product ID {product_id} tidak ditemukan dalam dataset.")
product_idx = product_idx[0]
sim_scores = list(enumerate(similarity_matrix[product_idx]))
sim_scores = sorted(sim_scores, key=lambda x: x[1], reverse=True)
sim_scores = sim_scores[1:top_n + 1]
product_indices = [i[0] for i in sim_scores]
recommended_products = df.iloc[product_indices].copy()
recommended_products = recommended_products[['Product ID', 'Product Name', 'Brand', 'Category', 'Color', 'Size']]
return recommended_products
from sklearn.metrics import mean_squared_error
import numpy as np
def calculate_mse_rmse(predictions, actual_relevance):
mse = mean_squared_error(actual_relevance, predictions)
rmse = np.sqrt(mse)
return mse, rmse
def get_cosine_similarity_scores(product_id, df, similarity_matrix):
product_id = int(product_id)
product_idx = df[df['Product ID'] == product_id].index[0]
sim_scores = similarity_matrix[product_idx]
return sim_scores
def get_actual_relevance(sim_scores, threshold=0.5):
return [1 if score >= threshold else 0 for score in sim_scores]
def calculate_mse_rmse_for_all(df, similarity_matrix, top_n=5):
all_mse = []
all_rmse = []
for product_id in df['Product ID'].unique():
try:
sim_scores = get_cosine_similarity_scores(product_id, df, similarity_matrix)
actual_relevance = get_actual_relevance(sim_scores)
if len(sim_scores) == 0:
continue
mse, rmse = calculate_mse_rmse(sim_scores[:top_n], actual_relevance[:top_n]) #consider top_n only
all_mse.append(mse)
all_rmse.append(rmse)
except (ValueError, IndexError) as e:
print(f"Error processing product ID {product_id}: {e}")
continue
return np.mean(all_mse), np.mean(all_rmse)
product_id = 77
input_product = get_product_details(product_id, product_data)
recommended_products = recommend_products(product_id, product_data, cosine_sim)
print("Input Produk")
input_product = input_product.reset_index(drop=True)
display(input_product)
print("\nTop 5 Rekomendasi Produk:")
recommended_products = recommended_products.reset_index(drop=True)
display(recommended_products)
avg_mse, avg_rmse = calculate_mse_rmse_for_all(product_data, cosine_sim)
print(f"Rata-rata MSE pada seluruh produk: {avg_mse:.4f}")
print(f"Rata-rata RMSE pada seluruh produk: {avg_rmse:.4f}")
"""## 6. Modeling with Collaborative Filtering
### Model Training
"""
embedding_dim = 100
user_input = layers.Input(shape=(1,), name='user_input')
product_input = layers.Input(shape=(1,), name='product_input')
user_embedding = layers.Embedding(input_dim=len(user_encoder.classes_), output_dim=embedding_dim)(user_input)
product_embedding = layers.Embedding(input_dim=len(product_encoder.classes_), output_dim=embedding_dim)(product_input)
user_embedding = layers.Flatten()(user_embedding)
product_embedding = layers.Flatten()(product_embedding)
user_bias = layers.Embedding(input_dim=len(user_encoder.classes_), output_dim=1, name='user_bias')(user_input)
product_bias = layers.Embedding(input_dim=len(product_encoder.classes_), output_dim=1, name='product_bias')(product_input)
user_bias = layers.Flatten()(user_bias)
product_bias = layers.Flatten()(product_bias)
merged = layers.concatenate([user_embedding, product_embedding])
mlp_layer = layers.Dense(256, activation='relu')(merged)
mlp_layer = layers.Dense(128, activation='relu')(mlp_layer)
mlp_layer = layers.Dense(64, activation='relu')(mlp_layer)
mlp_layer = layers.Dropout(0.2)(mlp_layer)
output = layers.Dense(1, activation='linear')(mlp_layer)
output = layers.add([output, user_bias, product_bias])
model = models.Model(inputs=[user_input, product_input], outputs=output)
model.compile(optimizer='adam', loss='mean_squared_error')
model.summary()
history = model.fit([X_train[:, 0], X_train[:, 1]], y_train,
validation_data=([X_test[:, 0], X_test[:, 1]], y_test),
epochs=100, batch_size=64)
"""### Mendapatkan Rekomendasi Produk Fashion"""
def get_user_rated_products(user_id, product_data):
rated_products = product_data[['User ID', 'Product ID']].drop_duplicates()
rated_products = rated_products[rated_products['User ID'] == user_id]
rated_product_details = product_data[product_data['Product ID'].isin(rated_products['Product ID'])]
rated_product_details = rated_product_details[['Product ID', 'Product Name', 'Brand', 'Category', 'Price', 'Rating']]
rated_product_details = rated_product_details.sort_values(by='Rating', ascending=False)
rated_product_details = rated_product_details.reset_index(drop=True)
return rated_product_details
def get_recommendations(user_id, product_data, model, product_encoder, top_n=5):
user_encoded = user_encoder.transform([user_id])[0]
product_ids = np.arange(len(product_encoder.classes_))
user_product_combinations = np.array([[user_encoded, pid] for pid in product_ids])
predictions = model.predict([user_product_combinations[:, 0], user_product_combinations[:, 1]])
predicted_ratings_df = pd.DataFrame({
'product_encoded': product_ids,
'predicted_rating': predictions.flatten()
})
rated_products = product_data[product_data['User ID'] == user_id]['product_encoded']
predicted_ratings_df = predicted_ratings_df[~predicted_ratings_df['product_encoded'].isin(rated_products)]
recommended = predicted_ratings_df.nlargest(top_n, 'predicted_rating')
recommended_products = product_data[product_data['product_encoded'].isin(recommended['product_encoded'])]
return recommended_products[['Product ID', 'Product Name', 'Brand', 'Category', 'Price', 'Rating']]
user_id = 98
top_n = 5
rated_products = get_user_rated_products(user_id, product_data)
print(f"Produk yang telah diberi rating oleh User ID {user_id}:")
display(rated_products)
recommended_products = get_recommendations(user_id, product_data, model, product_encoder, top_n)
print(f"Top {top_n} rekomendasi untuk User ID {user_id}:")
display(recommended_products)
"""## 7. Model Evaluation
Seperti yang telah dijelaskan sebelumnya, metrik evaluasi yang digunakan adalah Mean Square Error (MAE) dan Root Mean Square Error (RMSE). Kedua metrik tersebut dipilih karena keduanya memberikan ukuran yang jelas tentang sejauh mana prediksi rating atau preferensi produk yang diberikan oleh model menyimpang dari rating atau preferensi asli pengguna.
- Mean Squared Error (MSE)
MSE menghitung rata-rata kuadrat dari selisih antara nilai prediksi dan nilai aktual.
Dengan mengkuadratkan selisih, MSE memberikan bobot yang lebih besar pada kesalahan yang besar. Ini berarti bahwa model akan lebih "dihukum" jika membuat prediksi yang jauh dari nilai sebenarnya.
MSE sering digunakan ketika kita ingin memberikan penalti yang lebih besar pada kesalahan yang besar, karena kesalahan yang besar dapat memiliki konsekuensi yang lebih signifikan.
\begin{equation}
\text{MSE} = \frac{1}{n} \sum_{i=1}^{n} (y_i - \hat{y}_i)^2
\end{equation}
Di mana:
\begin{aligned}
\text{MSE} & = \text{Mean Squared Error} \\
n & = \text{Jumlah data observasi} \\
y_i & = \text{Nilai aktual ke-} i \\
\hat{y}_i & = \text{Nilai prediksi ke-} i
\end{aligned}
- Root Mean Squared Error (RMSE)
RMSE (Root Mean Squared Error) adalah akar kuadrat dari MSE, yang memberikan ukuran rata-rata kesalahan prediksi dalam satuan yang sama dengan data asli.
RMSE mempertahankan sifat MSE yang memberikan penalti lebih besar pada kesalahan besar, namun dengan membuat hasilnya lebih mudah dipahami karena mengembalikan kesalahan ke skala yang lebih mudah diinterpretasikan. RMSE sering digunakan ketika kita ingin menilai akurasi model dengan mempertimbangkan besarnya kesalahan dalam konteks yang lebih terukur dan dapat dibandingkan langsung dengan data aslinya.
\begin{equation}
\text{RMSE} = \sqrt{\frac{1}{N} \sum_{i=1}^{N} \left( r_{i} - \hat{r}_{i} \right)^2}
\end{equation}
Di mana:
\begin{aligned}
\text{RMSE} & = \text{Root Mean Squared Error, metrik untuk mengukur kesalahan prediksi model.} \\
r_{i} & = \text{Nilai aktual untuk item ke-i, misalnya rating asli yang diberikan oleh pengguna.} \\
\hat{r}_{i} & = \text{Prediksi untuk item ke-i yang dihasilkan oleh model.} \\
N & = \text{Jumlah total sampel (data) yang digunakan dalam evaluasi.}
\end{aligned}
### Perbandingan Performa Content Based Filtering dan Collaborative Filtering
"""
avg_mse, avg_rmse = calculate_mse_rmse_for_all(product_data, cosine_sim)
y_pred = model.predict([X_test[:, 0], X_test[:, 1]])
mse = mean_squared_error(y_test, y_pred)
print("Evaluasi Content Based Filtering")
print(f"Rata-rata MSE pada seluruh produk: {avg_mse:.4f}")
print(f"Rata-rata RMSE pada seluruh produk: {avg_rmse:.4f}")
print("\nEvaluasi Collaborative Filtering")
mse = mean_squared_error(y_test, y_pred)
print(f'MSE: {mse:.4f}')
rmse = math.sqrt(mse)
print(f'RMSE: {rmse:.4f}')
"""Berdasarkan hasil evaluasi menggunakan MSE dan RMSE, sistem Content Based Filtering dengan menggunakan Cosine Similarity menunjukkan performa yang cukup baik dalam merekomendasikan produk yang relevan. Dengan rata-rata MSE sebesar 0.0711 dan RMSE sebesar 0.2550, sistem berhasil meminimalkan kesalahan prediksi, menunjukkan bahwa produk yang direkomendasikan memiliki tingkat relevansi yang cukup tinggi terhadap preferensi pengguna. Threshold similarity 0.5 yang diterapkan memastikan bahwa hanya produk dengan kemiripan yang signifikan yang dipertimbangkan, menjaga kualitas rekomendasi dan relevansi hasil yang diberikan.
Sedangkan pada metode NCF, MSE sebesar 1.7791 dan RMSE sebesar 1.3338 menunjukkan bahwa model memiliki akurasi yang cukup baik dalam memprediksi rating untuk produk yang belum dinilai oleh pengguna. Meskipun nilai MSE dan RMSE menunjukkan adanya ruang untuk perbaikan, model ini berhasil memanfaatkan pola perilaku rating pengguna sebelumnya untuk memberikan rekomendasi produk yang relevan. Ke depannya, improvisasi dapat dilakukan dengan memperbaiki arsitektur model, menambahkan fitur baru, atau menggunakan teknik regularisasi untuk mengurangi overfitting dan meningkatkan akurasi prediksi.
"""