Predicted progress! From awful to just "very bad"
This commit is contained in:
parent
07f60e449b
commit
65dfaec1a5
@ -6,8 +6,8 @@ import os
|
|||||||
router = APIRouter()
|
router = APIRouter()
|
||||||
|
|
||||||
# Инициализация сервиса
|
# Инициализация сервиса
|
||||||
MODEL_PATH = os.getenv("MODEL_PATH", "laptop_price_model.pkl")
|
MODEL_PATH = os.getenv("MODEL_PATH", "services/laptop_price_model.pkl")
|
||||||
FEATURE_COLUMNS_PATH = os.getenv("FEATURE_COLUMNS_PATH", "feature_columns.pkl")
|
FEATURE_COLUMNS_PATH = os.getenv("FEATURE_COLUMNS_PATH", "services/feature_columns.pkl")
|
||||||
laptop_service = LaptopService(model_path=MODEL_PATH, feature_columns_path=FEATURE_COLUMNS_PATH)
|
laptop_service = LaptopService(model_path=MODEL_PATH, feature_columns_path=FEATURE_COLUMNS_PATH)
|
||||||
|
|
||||||
@router.post("/predict_price/", response_model=PredictPriceResponse, summary="Predict laptop price", description="Predict the price of a laptop based on its specifications.", response_description="The predicted price of the laptop.")
|
@router.post("/predict_price/", response_model=PredictPriceResponse, summary="Predict laptop price", description="Predict the price of a laptop based on its specifications.", response_description="The predicted price of the laptop.")
|
||||||
|
Binary file not shown.
Binary file not shown.
@ -1,12 +1,19 @@
|
|||||||
from sqlalchemy import Column, Integer, String, Float
|
from sqlalchemy import Column, Integer, Float, String
|
||||||
from database import Base
|
from database import Base
|
||||||
|
|
||||||
|
|
||||||
class Laptop(Base):
|
class Laptop(Base):
|
||||||
__tablename__ = "laptops"
|
__tablename__ = "laptops"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
|
brand = Column(String, index=True)
|
||||||
processor = Column(String, index=True)
|
processor = Column(String, index=True)
|
||||||
ram = Column(Integer)
|
ram = Column(Integer)
|
||||||
os = Column(String, index=True)
|
os = Column(String, index=True)
|
||||||
ssd = Column(Integer)
|
ssd = Column(Integer)
|
||||||
display = Column(Float)
|
display = Column(Float)
|
||||||
|
gpu = Column(String, index=True)
|
||||||
|
weight = Column(Float)
|
||||||
|
battery_size = Column(Integer)
|
||||||
|
release_year = Column(Integer)
|
||||||
|
display_type = Column(String, index=True)
|
||||||
|
@ -1,9 +1,10 @@
|
|||||||
fastapi
|
fastapi~=0.115.2
|
||||||
uvicorn
|
uvicorn
|
||||||
sqlalchemy
|
sqlalchemy~=2.0.35
|
||||||
psycopg2-binary
|
psycopg2-binary
|
||||||
pydantic
|
pydantic~=2.9.2
|
||||||
joblib
|
joblib~=1.4.2
|
||||||
pandas
|
pandas~=2.2.3
|
||||||
numpy
|
numpy~=2.1.2
|
||||||
python-dotenv
|
python-dotenv
|
||||||
|
scikit-learn~=1.5.2
|
@ -1,19 +1,32 @@
|
|||||||
from pydantic import BaseModel
|
from pydantic import BaseModel
|
||||||
|
from typing import Optional
|
||||||
|
|
||||||
class LaptopCreate(BaseModel):
|
class LaptopCreate(BaseModel):
|
||||||
|
brand: str
|
||||||
processor: str
|
processor: str
|
||||||
ram: int
|
ram: int
|
||||||
os: str
|
os: str
|
||||||
ssd: int
|
ssd: int
|
||||||
display: float
|
display: float
|
||||||
|
gpu: str
|
||||||
|
weight: float
|
||||||
|
battery_size: int
|
||||||
|
release_year: int
|
||||||
|
display_type: str
|
||||||
|
|
||||||
class LaptopResponse(BaseModel):
|
class LaptopResponse(BaseModel):
|
||||||
id: int
|
id: int
|
||||||
|
brand: str
|
||||||
processor: str
|
processor: str
|
||||||
ram: int
|
ram: int
|
||||||
os: str
|
os: str
|
||||||
ssd: int
|
ssd: int
|
||||||
display: float
|
display: float
|
||||||
|
gpu: str
|
||||||
|
weight: float
|
||||||
|
battery_size: int
|
||||||
|
release_year: int
|
||||||
|
display_type: str
|
||||||
|
|
||||||
class Config:
|
class Config:
|
||||||
orm_mode = True
|
orm_mode = True
|
||||||
|
26
services/ml/feature_importances.py
Normal file
26
services/ml/feature_importances.py
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import joblib
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
from services.ml.modelBuilder import X_train
|
||||||
|
|
||||||
|
# Загрузка модели и признаков
|
||||||
|
model_rf = joblib.load('laptop_price_model.pkl')
|
||||||
|
feature_columns = joblib.load('feature_columns.pkl')
|
||||||
|
|
||||||
|
# Получение важности признаков
|
||||||
|
importances = model_rf.feature_importances_
|
||||||
|
indices = np.argsort(importances)[::-1]
|
||||||
|
|
||||||
|
# Вывод наиболее важных признаков
|
||||||
|
print("Важность признаков:")
|
||||||
|
for f in range(X_train.shape[1]):
|
||||||
|
print(f"{f + 1}. {feature_columns[indices[f]]} ({importances[indices[f]]})")
|
||||||
|
|
||||||
|
# Визуализация важности признаков
|
||||||
|
plt.figure(figsize=(12, 8))
|
||||||
|
plt.title("Важность признаков (Random Forest)")
|
||||||
|
plt.bar(range(X_train.shape[1]), importances[indices], align='center')
|
||||||
|
plt.xticks(range(X_train.shape[1]), [feature_columns[i] for i in indices], rotation=90)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.show()
|
204
services/ml/generate_synthetic_data.py
Normal file
204
services/ml/generate_synthetic_data.py
Normal file
@ -0,0 +1,204 @@
|
|||||||
|
import pandas as pd
|
||||||
|
import numpy as np
|
||||||
|
import random
|
||||||
|
import re
|
||||||
|
from datetime import datetime
|
||||||
|
|
||||||
|
# Установка случайного зерна для воспроизводимости
|
||||||
|
np.random.seed(42)
|
||||||
|
random.seed(42)
|
||||||
|
|
||||||
|
# Определение возможных значений для категориальных признаков
|
||||||
|
brands = ['Dell', 'HP', 'Lenovo', 'Apple', 'Asus', 'Acer', 'MSI', 'Microsoft', 'Samsung', 'Toshiba']
|
||||||
|
processors = [
|
||||||
|
'Intel Core i3 10th Gen', 'Intel Core i5 10th Gen', 'Intel Core i7 10th Gen',
|
||||||
|
'AMD Ryzen 3 4000 Series', 'AMD Ryzen 5 4000 Series', 'AMD Ryzen 7 4000 Series'
|
||||||
|
]
|
||||||
|
oss = ['Windows 10', 'Windows 11', 'macOS', 'Linux']
|
||||||
|
gpus = ['Integrated', 'NVIDIA GeForce GTX 1650', 'NVIDIA GeForce RTX 3060', 'AMD Radeon RX 5600M']
|
||||||
|
display_sizes = [13.3, 14.0, 15.6, 17.3]
|
||||||
|
display_types = ['HD', 'Full HD', '4K', 'OLED']
|
||||||
|
ram_options = [4, 8, 16, 32] # в GB
|
||||||
|
ssd_options = [0, 256, 512, 1024] # в GB
|
||||||
|
weights = [1.2, 1.5, 2.0, 2.5, 3.0] # в кг
|
||||||
|
battery_sizes = [45, 60, 70, 90, 100] # в Вт⋅ч
|
||||||
|
release_years = list(range(2015, datetime.now().year + 1)) # от 2015 до текущего года
|
||||||
|
|
||||||
|
|
||||||
|
# Функции для генерации признаков
|
||||||
|
def generate_brand():
|
||||||
|
return random.choice(brands)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_processor():
|
||||||
|
return random.choice(processors)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_os():
|
||||||
|
return random.choice(oss)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_gpu():
|
||||||
|
return random.choice(gpus)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_display():
|
||||||
|
return random.choice(display_sizes)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_display_type():
|
||||||
|
return random.choice(display_types)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_ram():
|
||||||
|
return random.choice(ram_options)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_ssd():
|
||||||
|
return random.choice(ssd_options)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_weight():
|
||||||
|
return random.choice(weights)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_battery_size():
|
||||||
|
return random.choice(battery_sizes)
|
||||||
|
|
||||||
|
|
||||||
|
def generate_release_year():
|
||||||
|
return random.choice(release_years)
|
||||||
|
|
||||||
|
|
||||||
|
# Функция для расчёта цены
|
||||||
|
def calculate_price(brand, processor, ram, os, ssd, display, gpu, weight, battery_size, release_year, display_type):
|
||||||
|
base_price = 30000 # базовая цена в условных единицах
|
||||||
|
|
||||||
|
# Добавление стоимости в зависимости от бренда
|
||||||
|
brand_premium = {
|
||||||
|
'Apple': 40000,
|
||||||
|
'MSI': 35000,
|
||||||
|
'Dell': 15000,
|
||||||
|
'HP': 12000,
|
||||||
|
'Lenovo': 10000,
|
||||||
|
'Microsoft': 18000,
|
||||||
|
'Asus': 8000,
|
||||||
|
'Acer': 7000,
|
||||||
|
'Samsung': 9000,
|
||||||
|
'Toshiba': 8500
|
||||||
|
}
|
||||||
|
base_price += brand_premium.get(brand, 10000) # дефолтный премиум
|
||||||
|
|
||||||
|
# Добавление стоимости в зависимости от процессора
|
||||||
|
if 'i3' in processor or 'Ryzen 3' in processor:
|
||||||
|
base_price += 5000
|
||||||
|
elif 'i5' in processor or 'Ryzen 5' in processor:
|
||||||
|
base_price += 10000
|
||||||
|
elif 'i7' in processor or 'Ryzen 7' in processor:
|
||||||
|
base_price += 15000
|
||||||
|
|
||||||
|
# Добавление стоимости за RAM
|
||||||
|
base_price += ram * 2000 # 2000 условных единиц за каждый GB RAM
|
||||||
|
|
||||||
|
# Добавление стоимости за ОС
|
||||||
|
if os == 'Windows 11':
|
||||||
|
base_price += 5000
|
||||||
|
elif os == 'macOS':
|
||||||
|
base_price += 15000
|
||||||
|
elif os == 'Linux':
|
||||||
|
base_price += 3000
|
||||||
|
# Windows 10 считается стандартной и не добавляет стоимости
|
||||||
|
|
||||||
|
# Добавление стоимости за SSD
|
||||||
|
if ssd > 0:
|
||||||
|
base_price += ssd * 100 # 100 условных единиц за каждый GB SSD
|
||||||
|
|
||||||
|
# Добавление стоимости за размер дисплея
|
||||||
|
base_price += (display - 13) * 5000 # 5000 условных единиц за каждый дюйм больше 13"
|
||||||
|
|
||||||
|
# Добавление стоимости за тип дисплея
|
||||||
|
display_type_premium = {
|
||||||
|
'HD': 0,
|
||||||
|
'Full HD': 5000,
|
||||||
|
'4K': 15000,
|
||||||
|
'OLED': 20000
|
||||||
|
}
|
||||||
|
base_price += display_type_premium.get(display_type, 0)
|
||||||
|
|
||||||
|
# Добавление стоимости за GPU
|
||||||
|
gpu_premium = {
|
||||||
|
'Integrated': 0,
|
||||||
|
'NVIDIA GeForce GTX 1650': 15000,
|
||||||
|
'NVIDIA GeForce RTX 3060': 25000,
|
||||||
|
'AMD Radeon RX 5600M': 20000
|
||||||
|
}
|
||||||
|
base_price += gpu_premium.get(gpu, 0)
|
||||||
|
|
||||||
|
# Добавление стоимости за вес (легкие ноутбуки дороже)
|
||||||
|
base_price += (3.0 - weight) * 5000 # Чем легче, тем дороже
|
||||||
|
|
||||||
|
# Добавление стоимости за размер батареи
|
||||||
|
base_price += battery_size * 100 # 100 условных единиц за каждый Вт⋅ч батареи
|
||||||
|
|
||||||
|
# Добавление стоимости за год выпуска (новые модели дороже)
|
||||||
|
current_year = datetime.now().year
|
||||||
|
base_price += (current_year - release_year) * 2000 # 2000 условных единиц за каждый год назад
|
||||||
|
|
||||||
|
# Добавление случайного шума для реалистичности
|
||||||
|
noise = np.random.normal(0, 5000) # среднее 0, стандартное отклонение 5000
|
||||||
|
final_price = base_price + noise
|
||||||
|
|
||||||
|
return max(round(final_price, 2), 5000) # минимальная цена 5000 условных единиц
|
||||||
|
|
||||||
|
|
||||||
|
# Функция для генерации синтетических данных
|
||||||
|
def generate_synthetic_data(num_samples=100000):
|
||||||
|
data = []
|
||||||
|
for _ in range(num_samples):
|
||||||
|
brand = generate_brand()
|
||||||
|
processor = generate_processor()
|
||||||
|
os = generate_os()
|
||||||
|
gpu = generate_gpu()
|
||||||
|
display = generate_display()
|
||||||
|
display_type = generate_display_type()
|
||||||
|
ram = generate_ram()
|
||||||
|
ssd = generate_ssd()
|
||||||
|
weight = generate_weight()
|
||||||
|
battery_size = generate_battery_size()
|
||||||
|
release_year = generate_release_year()
|
||||||
|
|
||||||
|
price = calculate_price(
|
||||||
|
brand, processor, ram, os, ssd, display, gpu, weight, battery_size, release_year, display_type
|
||||||
|
)
|
||||||
|
|
||||||
|
data.append({
|
||||||
|
'brand': brand,
|
||||||
|
'processor': processor,
|
||||||
|
'ram': ram,
|
||||||
|
'os': os,
|
||||||
|
'ssd': ssd,
|
||||||
|
'display': display,
|
||||||
|
'gpu': gpu,
|
||||||
|
'weight': weight,
|
||||||
|
'battery_size': battery_size,
|
||||||
|
'release_year': release_year,
|
||||||
|
'display_type': display_type,
|
||||||
|
'price': price
|
||||||
|
})
|
||||||
|
return pd.DataFrame(data)
|
||||||
|
|
||||||
|
|
||||||
|
print("Генерация синтетических данных...")
|
||||||
|
synthetic_df = generate_synthetic_data(num_samples=100000)
|
||||||
|
|
||||||
|
# Просмотр первых нескольких строк
|
||||||
|
print("\nПример данных после генерации:")
|
||||||
|
print(synthetic_df.head())
|
||||||
|
|
||||||
|
# Проверка распределения цен
|
||||||
|
print("\nСтатистика по ценам:")
|
||||||
|
print(synthetic_df['price'].describe())
|
||||||
|
|
||||||
|
# Сохранение в CSV
|
||||||
|
synthetic_df.to_csv('synthetic_laptops.csv', index=False)
|
||||||
|
print("\nСинтетические данные сохранены в 'synthetic_laptops.csv'.")
|
123
services/ml/modelBuilder.py
Normal file
123
services/ml/modelBuilder.py
Normal file
@ -0,0 +1,123 @@
|
|||||||
|
import pandas as pd
|
||||||
|
from sklearn.model_selection import train_test_split
|
||||||
|
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
|
||||||
|
from sklearn.linear_model import LinearRegression
|
||||||
|
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
|
||||||
|
import matplotlib.pyplot as plt
|
||||||
|
import joblib
|
||||||
|
import numpy as np
|
||||||
|
|
||||||
|
# Шаг 1: Загрузка данных
|
||||||
|
df = pd.read_csv('../../datasets/synthetic_laptops.csv') # Убедитесь, что путь к файлу правильный
|
||||||
|
|
||||||
|
# Шаг 2: Проверка и очистка имен столбцов
|
||||||
|
print("Имена столбцов до очистки:")
|
||||||
|
print(df.columns.tolist())
|
||||||
|
|
||||||
|
# Приведение имен столбцов к нижнему регистру и удаление пробелов
|
||||||
|
df.columns = df.columns.str.strip().str.lower()
|
||||||
|
print("\nИмена столбцов после очистки:")
|
||||||
|
print(df.columns.tolist())
|
||||||
|
|
||||||
|
# Шаг 3: Проверка наличия необходимых столбцов
|
||||||
|
required_columns = [
|
||||||
|
'brand', 'processor', 'ram', 'os', 'ssd', 'display',
|
||||||
|
'gpu', 'weight', 'battery_size', 'release_year', 'display_type', 'price'
|
||||||
|
]
|
||||||
|
missing_columns = [col for col in required_columns if col not in df.columns]
|
||||||
|
|
||||||
|
if missing_columns:
|
||||||
|
print(f"\nОтсутствуют следующие столбцы: {missing_columns}")
|
||||||
|
raise Exception(f"Отсутствуют столбцы: {missing_columns}")
|
||||||
|
else:
|
||||||
|
print("\nВсе необходимые столбцы присутствуют.")
|
||||||
|
|
||||||
|
# Шаг 4: Удаление строк с пропущенными значениями
|
||||||
|
df = df.dropna(subset=required_columns)
|
||||||
|
print(f"\nКоличество строк после удаления пропусков: {df.shape[0]}")
|
||||||
|
|
||||||
|
# Шаг 5: Очистка и преобразование колонок
|
||||||
|
|
||||||
|
# Функция для очистки числовых колонок, если они строковые
|
||||||
|
def clean_numeric_column(column, remove_chars=['₹', ',', ' ']):
|
||||||
|
if column.dtype == object:
|
||||||
|
for char in remove_chars:
|
||||||
|
column = column.str.replace(char, '', regex=False)
|
||||||
|
return pd.to_numeric(column, errors='coerce')
|
||||||
|
else:
|
||||||
|
return column
|
||||||
|
|
||||||
|
# Очистка числовых колонок (исключая 'price', если уже числовая)
|
||||||
|
numerical_columns = ['ram', 'ssd', 'display', 'weight', 'battery_size', 'release_year']
|
||||||
|
for col in numerical_columns:
|
||||||
|
df[col] = clean_numeric_column(df[col])
|
||||||
|
|
||||||
|
# Проверка на пропущенные значения после очистки
|
||||||
|
df = df.dropna(subset=['price'] + numerical_columns)
|
||||||
|
print(f"\nКоличество строк после очистки числовых колонок: {df.shape[0]}")
|
||||||
|
|
||||||
|
# Шаг 6: Выбор необходимых столбцов (все уже включены)
|
||||||
|
# df уже содержит все необходимые столбцы
|
||||||
|
|
||||||
|
print("\nПример данных после предобработки:")
|
||||||
|
print(df.head())
|
||||||
|
|
||||||
|
# Шаг 7: Преобразование категориальных переменных с помощью One-Hot Encoding
|
||||||
|
categorical_features = ['brand', 'processor', 'os', 'gpu', 'display_type']
|
||||||
|
df = pd.get_dummies(df, columns=categorical_features, drop_first=True)
|
||||||
|
print("\nИмена колонок после One-Hot Encoding:")
|
||||||
|
print(df.columns.tolist())
|
||||||
|
|
||||||
|
# Шаг 8: Разделение данных на обучающую и тестовую выборки
|
||||||
|
X = df.drop('price', axis=1)
|
||||||
|
y = df['price']
|
||||||
|
|
||||||
|
X_train, X_test, y_train, y_test = train_test_split(
|
||||||
|
X, y, test_size=0.2, random_state=42
|
||||||
|
)
|
||||||
|
|
||||||
|
print(f"\nРазмер обучающей выборки: {X_train.shape}")
|
||||||
|
print(f"Размер тестовой выборки: {X_test.shape}")
|
||||||
|
|
||||||
|
# Шаг 9: Обучение моделей
|
||||||
|
model_rf = RandomForestRegressor(n_estimators=100, random_state=42)
|
||||||
|
model_rf.fit(X_train, y_train)
|
||||||
|
|
||||||
|
print("\nМодели успешно обучены.")
|
||||||
|
|
||||||
|
# Шаг 10: Оценка моделей
|
||||||
|
models = {
|
||||||
|
'Random Forest': model_rf,
|
||||||
|
}
|
||||||
|
|
||||||
|
for name, mdl in models.items():
|
||||||
|
y_pred = mdl.predict(X_test)
|
||||||
|
mae = mean_absolute_error(y_test, y_pred)
|
||||||
|
rmse = mean_squared_error(y_test, y_pred, squared=False)
|
||||||
|
r2 = r2_score(y_test, y_pred)
|
||||||
|
print(f"{name} - MAE: {mae}, RMSE: {rmse}, R²: {r2}")
|
||||||
|
|
||||||
|
# Шаг 11: Сохранение модели и списка признаков
|
||||||
|
joblib.dump(model_rf, 'laptop_price_model.pkl')
|
||||||
|
print("\nМодель Random Forest сохранена как 'laptop_price_model.pkl'.")
|
||||||
|
|
||||||
|
feature_columns = X.columns.tolist()
|
||||||
|
joblib.dump(feature_columns, 'feature_columns.pkl')
|
||||||
|
print("Сохранены названия признаков в 'feature_columns.pkl'.")
|
||||||
|
|
||||||
|
# Получение важности признаков
|
||||||
|
importances = model_rf.feature_importances_
|
||||||
|
indices = np.argsort(importances)[::-1]
|
||||||
|
|
||||||
|
# Вывод наиболее важных признаков
|
||||||
|
print("Важность признаков:")
|
||||||
|
for f in range(X_train.shape[1]):
|
||||||
|
print(f"{f + 1}. {feature_columns[indices[f]]} ({importances[indices[f]]})")
|
||||||
|
|
||||||
|
# Визуализация важности признаков
|
||||||
|
plt.figure(figsize=(12, 8))
|
||||||
|
plt.title("Важность признаков (Random Forest)")
|
||||||
|
plt.bar(range(X_train.shape[1]), importances[indices], align='center')
|
||||||
|
plt.xticks(range(X_train.shape[1]), [feature_columns[i] for i in indices], rotation=90)
|
||||||
|
plt.tight_layout()
|
||||||
|
plt.show()
|
@ -1,143 +0,0 @@
|
|||||||
import pandas as pd
|
|
||||||
from sklearn.linear_model import LinearRegression
|
|
||||||
from sklearn.model_selection import train_test_split
|
|
||||||
from sklearn.ensemble import RandomForestRegressor, GradientBoostingRegressor
|
|
||||||
from sklearn.metrics import mean_absolute_error, mean_squared_error, r2_score
|
|
||||||
import joblib
|
|
||||||
import re
|
|
||||||
|
|
||||||
# Шаг 1: Загрузка данных
|
|
||||||
df = pd.read_csv('../laptops.csv')
|
|
||||||
|
|
||||||
# Шаг 2: Проверка и очистка имен столбцов
|
|
||||||
print("Имена столбцов до очистки:")
|
|
||||||
print(df.columns.tolist())
|
|
||||||
|
|
||||||
# Приведение имен столбцов к нижнему регистру и удаление пробелов
|
|
||||||
df.columns = df.columns.str.strip().str.lower()
|
|
||||||
print("\nИмена столбцов после очистки:")
|
|
||||||
print(df.columns.tolist())
|
|
||||||
|
|
||||||
# Шаг 3: Переименование столбцов (если необходимо)
|
|
||||||
df = df.rename(columns={
|
|
||||||
'processor': 'processor',
|
|
||||||
'ram': 'ram',
|
|
||||||
'os': 'os',
|
|
||||||
'ssd': 'ssd',
|
|
||||||
'display': 'display',
|
|
||||||
'price': 'price'
|
|
||||||
# Другие столбцы можно оставить без изменений или переименовать по необходимости
|
|
||||||
})
|
|
||||||
|
|
||||||
# Шаг 4: Проверка наличия необходимых столбцов
|
|
||||||
required_columns = ['processor', 'ram', 'os', 'ssd', 'display', 'price']
|
|
||||||
missing_columns = [col for col in required_columns if col not in df.columns]
|
|
||||||
|
|
||||||
if missing_columns:
|
|
||||||
print(f"\nОтсутствуют следующие столбцы: {missing_columns}")
|
|
||||||
# Здесь можно добавить дополнительную обработку или завершить выполнение
|
|
||||||
raise Exception(f"Отсутствуют столбцы: {missing_columns}")
|
|
||||||
else:
|
|
||||||
print("\nВсе необходимые столбцы присутствуют.")
|
|
||||||
|
|
||||||
# Шаг 5: Удаление строк с пропущенными значениями
|
|
||||||
df = df.dropna(subset=required_columns)
|
|
||||||
print(f"\nКоличество строк после удаления пропусков: {df.shape[0]}")
|
|
||||||
|
|
||||||
# Шаг 6: Очистка и преобразование колонок
|
|
||||||
|
|
||||||
# Функция для очистки числовых колонок, содержащих символы
|
|
||||||
def clean_numeric_column(column, remove_chars=['₹', ',', ' ']):
|
|
||||||
for char in remove_chars:
|
|
||||||
column = column.str.replace(char, '', regex=False)
|
|
||||||
return pd.to_numeric(column, errors='coerce')
|
|
||||||
|
|
||||||
# Очистка колонки 'price'
|
|
||||||
df['price'] = clean_numeric_column(df['price'])
|
|
||||||
|
|
||||||
# Очистка колонки 'ram' (например, '16 GB DDR4 RAM' -> 16)
|
|
||||||
def extract_numeric_ram(ram_str):
|
|
||||||
match = re.search(r'(\d+)', ram_str)
|
|
||||||
if match:
|
|
||||||
return int(match.group(1))
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
df['ram'] = df['ram'].apply(extract_numeric_ram)
|
|
||||||
|
|
||||||
# Очистка колонки 'ssd' (например, '512 GB SSD' -> 512)
|
|
||||||
def extract_numeric_ssd(ssd_str):
|
|
||||||
match = re.search(r'(\d+)', ssd_str)
|
|
||||||
if match:
|
|
||||||
return int(match.group(1))
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
df['ssd'] = df['ssd'].apply(extract_numeric_ssd)
|
|
||||||
|
|
||||||
# Очистка колонки 'display' (убираем лишние символы, если есть)
|
|
||||||
def clean_display(display_str):
|
|
||||||
match = re.search(r'([\d.]+)', display_str)
|
|
||||||
if match:
|
|
||||||
return float(match.group(1))
|
|
||||||
else:
|
|
||||||
return None
|
|
||||||
|
|
||||||
df['display'] = df['display'].apply(clean_display)
|
|
||||||
|
|
||||||
# Проверка на пропущенные значения после очистки
|
|
||||||
df = df.dropna(subset=['price', 'ram', 'ssd', 'display'])
|
|
||||||
print(f"\nКоличество строк после очистки числовых колонок: {df.shape[0]}")
|
|
||||||
|
|
||||||
# Шаг 7: Выбор необходимых столбцов
|
|
||||||
df = df[required_columns]
|
|
||||||
print("\nПример данных после предобработки:")
|
|
||||||
print(df.head())
|
|
||||||
|
|
||||||
# Шаг 8: Преобразование категориальных переменных с помощью One-Hot Encoding
|
|
||||||
df = pd.get_dummies(df, columns=['processor', 'os'], drop_first=True)
|
|
||||||
print("\nИмена колонок после One-Hot Encoding:")
|
|
||||||
print(df.columns.tolist())
|
|
||||||
|
|
||||||
# Шаг 9: Разделение данных на обучающую и тестовую выборки
|
|
||||||
X = df.drop('price', axis=1)
|
|
||||||
y = df['price']
|
|
||||||
|
|
||||||
X_train, X_test, y_train, y_test = train_test_split(
|
|
||||||
X, y, test_size=0.2, random_state=42
|
|
||||||
)
|
|
||||||
|
|
||||||
print(f"\nРазмер обучающей выборки: {X_train.shape}")
|
|
||||||
print(f"Размер тестовой выборки: {X_test.shape}")
|
|
||||||
|
|
||||||
# Шаг 10: Обучение модели
|
|
||||||
model = RandomForestRegressor(n_estimators=100, random_state=42)
|
|
||||||
model.fit(X_train, y_train)
|
|
||||||
|
|
||||||
# Обучение моделей
|
|
||||||
gbr = GradientBoostingRegressor(n_estimators=100, random_state=42)
|
|
||||||
gbr.fit(X_train, y_train)
|
|
||||||
|
|
||||||
lr = LinearRegression()
|
|
||||||
lr.fit(X_train, y_train)
|
|
||||||
print("\nМодели успешно обучена.")
|
|
||||||
|
|
||||||
# Оценка моделей
|
|
||||||
|
|
||||||
models = {'Random Forest': model, 'Gradient Boosting': gbr, 'Linear Regression': lr}
|
|
||||||
|
|
||||||
for name, mdl in models.items():
|
|
||||||
y_pred = mdl.predict(X_test)
|
|
||||||
mae = mean_absolute_error(y_test, y_pred)
|
|
||||||
rmse = mean_squared_error(y_test, y_pred, squared=False)
|
|
||||||
r2 = r2_score(y_test, y_pred)
|
|
||||||
print(f"{name} - MAE: {mae}, RMSE: {rmse}, R²: {r2}")
|
|
||||||
|
|
||||||
# Шаг 12: Сохранение модели
|
|
||||||
joblib.dump(model, '../laptop_price_model.pkl')
|
|
||||||
print("\nМодель сохранена как 'laptop_price_model.pkl'.")
|
|
||||||
|
|
||||||
# Дополнительно: Сохранение колонок, полученных после One-Hot Encoding, для использования в бэкенде
|
|
||||||
feature_columns = X.columns.tolist()
|
|
||||||
joblib.dump(feature_columns, '../feature_columns.pkl')
|
|
||||||
print("Сохранены названия признаков в 'feature_columns.pkl'.")
|
|
Loading…
Reference in New Issue
Block a user