Compare commits

..

No commits in common. "07f60e449b1db3c84c0712f92af11f2336564b58" and "f294a3d33d14fc7b9e8cd7f7230fbf24795a84d7" have entirely different histories.

10 changed files with 103 additions and 112 deletions

View File

@ -1,7 +1,4 @@
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="Black">
<option name="sdkName" value="Python 3.12 (price-builder-backend)" />
</component>
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.12 (price-builder-backend)" project-jdk-type="Python SDK" />
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (venv)" project-jdk-type="Python SDK" />
</project>

View File

@ -4,7 +4,7 @@
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/venv" />
</content>
<orderEntry type="jdk" jdkName="Python 3.12 (price-builder-backend)" jdkType="Python SDK" />
<orderEntry type="jdk" jdkName="Python 3.9 (venv)" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View File

@ -1,27 +0,0 @@
from fastapi import APIRouter, HTTPException
from schemas.schemas import LaptopCreate, LaptopResponse, PredictPriceResponse
from services.service import LaptopService
import os
router = APIRouter()
# Инициализация сервиса
MODEL_PATH = os.getenv("MODEL_PATH", "laptop_price_model.pkl")
FEATURE_COLUMNS_PATH = os.getenv("FEATURE_COLUMNS_PATH", "feature_columns.pkl")
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.")
def predict_price(data: LaptopCreate):
"""
Predict the price of a laptop given its specifications.
- **processor**: Type of processor (e.g., i5, i7)
- **ram**: Amount of RAM in GB
- **os**: Operating system (e.g., Windows, MacOS)
- **ssd**: Size of SSD in GB
- **display**: Size of the display in inches
"""
try:
return laptop_service.predict_price(data.dict())
except Exception as e:
raise HTTPException(status_code=400, detail=str(e))

80
main.py
View File

@ -1,7 +1,79 @@
from fastapi import FastAPI
from controllers import controller
from fastapi import FastAPI, Depends, HTTPException
from sqlalchemy.orm import Session
from typing import List
from models import Laptop
from schemas import LaptopCreate, LaptopResponse
from database import SessionLocal, engine, Base
from pydantic import BaseModel
import joblib
import pandas as pd
import numpy as np
# Загрузка модели и списка признаков
model = joblib.load('laptop_price_model.pkl')
feature_columns = joblib.load('feature_columns.pkl')
app = FastAPI()
# Подключение маршрутов
app.include_router(controller.router)
# Определение Pydantic модели для входных данных
class LaptopData(BaseModel):
processor: str
ram: int
os: str
ssd: int
display: float
def get_db():
db = SessionLocal()
try:
yield db
finally:
db.close()
@app.post("/laptops/", response_model=LaptopResponse)
def create_laptop(laptop: LaptopCreate, db: Session = Depends(get_db)):
db_laptop = Laptop(**laptop.dict())
db.add(db_laptop)
db.commit()
db.refresh(db_laptop)
return db_laptop
@app.get("/laptops/", response_model=List[LaptopResponse])
def read_laptops(skip: int = 0, limit: int = 10, db: Session = Depends(get_db)):
laptops = db.query(Laptop).offset(skip).limit(limit).all()
return laptops
@app.get("/laptops/{laptop_id}", response_model=LaptopResponse)
def read_laptop(laptop_id: int, db: Session = Depends(get_db)):
laptop = db.query(Laptop).filter(Laptop.id == laptop_id).first()
if laptop is None:
raise HTTPException(status_code=404, detail="Laptop not found")
return laptop
# Эндпоинт для предсказания цены
@app.post("/predict_price/")
def predict_price(data: LaptopData):
input_data = data.dict()
# Преобразование данных в DataFrame
input_df = pd.DataFrame([input_data])
# Применение One-Hot Encoding к категориальным признакам
input_df = pd.get_dummies(input_df, columns=['processor', 'os'], drop_first=True)
# Добавление отсутствующих признаков, если они есть
for col in feature_columns:
if col not in input_df.columns and col != 'price':
input_df[col] = 0
# Упорядочивание колонок согласно обучающей выборке
input_df = input_df[feature_columns]
# Предсказание цены
predicted_price = model.predict(input_df)[0]
return {"predicted_price": round(predicted_price, 2)}

View File

@ -7,7 +7,7 @@ import joblib
import re
# Шаг 1: Загрузка данных
df = pd.read_csv('../laptops.csv')
df = pd.read_csv('laptops.csv')
# Шаг 2: Проверка и очистка имен столбцов
print("Имена столбцов до очистки:")
@ -134,10 +134,10 @@ for name, mdl in models.items():
print(f"{name} - MAE: {mae}, RMSE: {rmse}, R²: {r2}")
# Шаг 12: Сохранение модели
joblib.dump(model, '../laptop_price_model.pkl')
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')
joblib.dump(feature_columns, 'feature_columns.pkl')
print("Сохранены названия признаков в 'feature_columns.pkl'.")

View File

@ -5,8 +5,9 @@ class Laptop(Base):
__tablename__ = "laptops"
id = Column(Integer, primary_key=True, index=True)
processor = Column(String, index=True)
title = Column(String, index=True)
price = Column(Float)
processor = Column(String)
ram = Column(Integer)
os = Column(String, index=True)
ssd = Column(Integer)
display = Column(Float)

View File

@ -1,9 +0,0 @@
fastapi
uvicorn
sqlalchemy
psycopg2-binary
pydantic
joblib
pandas
numpy
python-dotenv

19
schemas.py Normal file
View File

@ -0,0 +1,19 @@
from pydantic import BaseModel
class LaptopBase(BaseModel):
title: str
price: float
processor: str
ram: int
ssd: int
display: float
class LaptopCreate(LaptopBase):
price: float
class LaptopResponse(LaptopBase):
id: int
price: float
class Config:
orm_mode = True

View File

@ -1,22 +0,0 @@
from pydantic import BaseModel
class LaptopCreate(BaseModel):
processor: str
ram: int
os: str
ssd: int
display: float
class LaptopResponse(BaseModel):
id: int
processor: str
ram: int
os: str
ssd: int
display: float
class Config:
orm_mode = True
class PredictPriceResponse(BaseModel):
predicted_price: float

View File

@ -1,40 +0,0 @@
import pandas as pd
import joblib
from typing import List, Dict
from schemas.schemas import LaptopCreate, LaptopResponse, PredictPriceResponse
class LaptopService:
def __init__(self, model_path: str, feature_columns_path: str):
try:
self.model = joblib.load(model_path)
except FileNotFoundError:
raise Exception(f"Model file not found at {model_path}")
except Exception as e:
raise Exception(f"Error loading model: {str(e)}")
try:
self.feature_columns = joblib.load(feature_columns_path)
except FileNotFoundError:
raise Exception(f"Feature columns file not found at {feature_columns_path}")
except Exception as e:
raise Exception(f"Error loading feature columns: {str(e)}")
def predict_price(self, data: Dict[str, any]) -> PredictPriceResponse:
# Преобразование данных в DataFrame
input_df = pd.DataFrame([data])
# Применение One-Hot Encoding к категориальным признакам
input_df = pd.get_dummies(input_df, columns=['processor', 'os'], drop_first=True)
# Добавление отсутствующих признаков, если они есть
for col in self.feature_columns:
if col not in input_df.columns and col != 'price':
input_df[col] = 0
# Упорядочивание колонок согласно обучающей выборке
input_df = input_df[self.feature_columns]
# Предсказание цены
predicted_price = self.model.predict(input_df)[0]
return PredictPriceResponse(predicted_price=round(predicted_price, 2))