{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " price_usd weight_kg rating reviews_count return_rate_pct\n", "0 53.65 0.35 3.9 204 0.0\n", "1 37.28 0.91 3.9 171 7.8\n", "2 69.96 1.66 3.7 249 7.0\n", "3 93.85 1.95 4.2 347 5.1\n", "4 36.30 1.10 3.3 130 11.8\n", " price_usd weight_kg rating reviews_count return_rate_pct\n", "count 400.00 400.00 400.00 400.00 400.00\n", "mean 51.54 1.50 3.53 201.16 9.99\n", "std 28.52 0.82 0.63 96.89 6.06\n", "min 5.00 0.10 1.80 1.00 0.00\n", "25% 29.36 0.91 3.10 131.75 5.18\n", "50% 51.34 1.47 3.50 202.50 9.80\n", "75% 69.16 2.01 3.90 266.50 14.10\n", "max 177.87 4.02 5.00 600.00 26.70\n", "\n", "Explained variance ratio: PC1=58.3% PC2=36.2% Total=94.6%\n", "Loadings (components):\n", " price_usd weight_kg rating reviews_count return_rate_pct\n", "PC1 0.544 0.089 0.539 0.564 -0.296\n", "PC2 0.236 0.721 -0.157 0.133 0.618\n", "\n", "Sample product → PC1=2.2478 PC2=-0.2246\n" ] }, { "name": "stderr", "output_type": "stream", "text": [ "/usr/local/lib/python3.12/dist-packages/sklearn/utils/validation.py:2732: UserWarning: X has feature names, but StandardScaler was fitted without feature names\n", " warnings.warn(\n" ] } ], "source": [ "import numpy as np\n", "import pandas as pd\n", "from sklearn.decomposition import PCA\n", "from sklearn.preprocessing import StandardScaler\n", "\n", "np.random.seed(42)\n", "n = 400\n", "\n", "# 5 correlated product features (PCA will find the variance structure)\n", "# Latent factor 1: 'quality' drives rating, reviews and price\n", "# Latent factor 2: 'size' drives weight and return_rate\n", "quality = np.random.normal(0, 1, n)\n", "size = np.random.normal(0, 1, n)\n", "\n", "price_usd = (50 + 30 * quality + 10 * size + np.random.normal(0, 5, n)).clip(5, 200).round(2)\n", "weight_kg = (1.5 + 0.2 * quality + 0.8 * size + np.random.normal(0, 0.2, n)).clip(0.1, 5).round(2)\n", "rating = (3.5 + 0.6 * quality - 0.1 * size + np.random.normal(0, 0.3, n)).clip(1, 5).round(1)\n", "reviews_count = (200 + 100 * quality + 20 * size + np.random.normal(0, 20, n)).clip(1, 600).round().astype(int)\n", "return_rate_pct= (10 - 3 * quality + 5 * size + np.random.normal(0, 2, n)).clip(0, 40).round(1)\n", "\n", "df = pd.DataFrame({\n", " 'price_usd': price_usd,\n", " 'weight_kg': weight_kg,\n", " 'rating': rating,\n", " 'reviews_count': reviews_count,\n", " 'return_rate_pct': return_rate_pct,\n", "})\n", "\n", "print(df.head())\n", "print(df.describe().round(2))\n", "\n", "X = df.values\n", "scaler = StandardScaler()\n", "X_scaled = scaler.fit_transform(X)\n", "\n", "pca = PCA(n_components=2)\n", "pca.fit(X_scaled)\n", "\n", "evr = pca.explained_variance_ratio_\n", "print(f'\\nExplained variance ratio: PC1={evr[0]:.1%} PC2={evr[1]:.1%} Total={evr.sum():.1%}')\n", "print('Loadings (components):')\n", "loadings = pd.DataFrame(pca.components_, columns=df.columns,\n", " index=['PC1', 'PC2'])\n", "print(loadings.round(3))\n", "\n", "# Sample transform\n", "sample = pd.DataFrame([[85, 1.2, 4.3, 350, 7.5]],\n", " columns=df.columns)\n", "coords = pca.transform(scaler.transform(sample))[0]\n", "print(f'\\nSample product → PC1={coords[0]:.4f} PC2={coords[1]:.4f}')" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "/usr/local/lib/python3.12/dist-packages/huggingface_hub/utils/_auth.py:122: UserWarning: \n", "Error while fetching `HF_TOKEN` secret value from your vault: 'Requesting secret HF_TOKEN timed out. Secrets can only be fetched when running from the Colab UI.'.\n", "You are not authenticated with the Hugging Face Hub in this notebook.\n", "If the error persists, please let us know by opening an issue on GitHub (https://github.com/huggingface/huggingface_hub/issues/new).\n", " warnings.warn(\n" ] } ], "source": [ "from huggingface_hub import notebook_login\n", "notebook_login()" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Model saved locally: /content/ML_PCA_ProductFeatures.joblib\n" ] }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b06818f86b664f51a7e6be2f4a53b9b1", "version_major": 2, "version_minor": 0 }, "text/plain": [ "Processing Files (0 / 0) : | | 0.00B / 0.00B " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "7c1d1c5ec809496da9b8e8430867dee9", "version_major": 2, "version_minor": 0 }, "text/plain": [ "New Data Upload : | | 0.00B / 0.00B " ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "bc49b06253d6487a86df021ec4cd5ffd", "version_major": 2, "version_minor": 0 }, "text/plain": [ " ...CA_ProductFeatures.joblib: 100%|##########| 1.72kB / 1.72kB " ] }, "metadata": {}, "output_type": "display_data" }, { "name": "stdout", "output_type": "stream", "text": [ "Uploaded to https://huggingface.co/looh2/model\n" ] } ], "source": [ "import joblib\n", "from pathlib import Path\n", "from huggingface_hub import HfApi\n", "\n", "repo_id = \"looh2/model\"\n", "model_path = Path(\"ML_PCA_ProductFeatures.joblib\")\n", "\n", "bundle = {\n", " 'scaler': scaler,\n", " 'pca': pca,\n", " 'explained_variance_ratio': evr.tolist(),\n", " 'feature_names': df.columns.tolist(),\n", "}\n", "joblib.dump(bundle, model_path)\n", "print(f\"Model saved locally: {model_path.resolve()}\")\n", "\n", "api = HfApi()\n", "api.create_repo(repo_id=repo_id, repo_type=\"model\", exist_ok=True)\n", "api.upload_file(\n", " path_or_fileobj=str(model_path),\n", " path_in_repo=model_path.name,\n", " repo_id=repo_id,\n", " repo_type=\"model\",\n", ")\n", "print(f\"Uploaded to https://huggingface.co/{repo_id}\")" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [ { "data": { "application/vnd.jupyter.widget-view+json": { "model_id": "b0563875e236413d9a9ff2924b559419", "version_major": 2, "version_minor": 0 }, "text/plain": [ "ML_PCA_ProductFeatures.joblib: 0%| | 0.00/1.72k [00:00