Compare commits
8 Commits
f294a3d33d
...
07f60e449b
Author | SHA1 | Date | |
---|---|---|---|
07f60e449b | |||
28d22ea47e | |||
e83da8582d | |||
6cbcb76867 | |||
7507a56237 | |||
98d2b8d19c | |||
847bb0694e | |||
a23b880375 |
@ -1,4 +1,7 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectRootManager" version="2" project-jdk-name="Python 3.9 (venv)" project-jdk-type="Python SDK" />
|
<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" />
|
||||||
</project>
|
</project>
|
@ -4,7 +4,7 @@
|
|||||||
<content url="file://$MODULE_DIR$">
|
<content url="file://$MODULE_DIR$">
|
||||||
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
<excludeFolder url="file://$MODULE_DIR$/venv" />
|
||||||
</content>
|
</content>
|
||||||
<orderEntry type="jdk" jdkName="Python 3.9 (venv)" jdkType="Python SDK" />
|
<orderEntry type="jdk" jdkName="Python 3.12 (price-builder-backend)" jdkType="Python SDK" />
|
||||||
<orderEntry type="sourceFolder" forTests="false" />
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
27
controllers/controller.py
Normal file
27
controllers/controller.py
Normal file
@ -0,0 +1,27 @@
|
|||||||
|
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
80
main.py
@ -1,79 +1,7 @@
|
|||||||
from fastapi import FastAPI, Depends, HTTPException
|
from fastapi import FastAPI
|
||||||
from sqlalchemy.orm import Session
|
from controllers import controller
|
||||||
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 = FastAPI()
|
||||||
|
|
||||||
# Определение Pydantic модели для входных данных
|
# Подключение маршрутов
|
||||||
class LaptopData(BaseModel):
|
app.include_router(controller.router)
|
||||||
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)}
|
|
||||||
|
@ -5,9 +5,8 @@ class Laptop(Base):
|
|||||||
__tablename__ = "laptops"
|
__tablename__ = "laptops"
|
||||||
|
|
||||||
id = Column(Integer, primary_key=True, index=True)
|
id = Column(Integer, primary_key=True, index=True)
|
||||||
title = Column(String, index=True)
|
processor = Column(String, index=True)
|
||||||
price = Column(Float)
|
|
||||||
processor = Column(String)
|
|
||||||
ram = Column(Integer)
|
ram = Column(Integer)
|
||||||
|
os = Column(String, index=True)
|
||||||
ssd = Column(Integer)
|
ssd = Column(Integer)
|
||||||
display = Column(Float)
|
display = Column(Float)
|
9
requirements.txt
Normal file
9
requirements.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
fastapi
|
||||||
|
uvicorn
|
||||||
|
sqlalchemy
|
||||||
|
psycopg2-binary
|
||||||
|
pydantic
|
||||||
|
joblib
|
||||||
|
pandas
|
||||||
|
numpy
|
||||||
|
python-dotenv
|
19
schemas.py
19
schemas.py
@ -1,19 +0,0 @@
|
|||||||
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
|
|
22
schemas/schemas.py
Normal file
22
schemas/schemas.py
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
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
|
@ -7,7 +7,7 @@ import joblib
|
|||||||
import re
|
import re
|
||||||
|
|
||||||
# Шаг 1: Загрузка данных
|
# Шаг 1: Загрузка данных
|
||||||
df = pd.read_csv('laptops.csv')
|
df = pd.read_csv('../laptops.csv')
|
||||||
|
|
||||||
# Шаг 2: Проверка и очистка имен столбцов
|
# Шаг 2: Проверка и очистка имен столбцов
|
||||||
print("Имена столбцов до очистки:")
|
print("Имена столбцов до очистки:")
|
||||||
@ -134,10 +134,10 @@ for name, mdl in models.items():
|
|||||||
print(f"{name} - MAE: {mae}, RMSE: {rmse}, R²: {r2}")
|
print(f"{name} - MAE: {mae}, RMSE: {rmse}, R²: {r2}")
|
||||||
|
|
||||||
# Шаг 12: Сохранение модели
|
# Шаг 12: Сохранение модели
|
||||||
joblib.dump(model, 'laptop_price_model.pkl')
|
joblib.dump(model, '../laptop_price_model.pkl')
|
||||||
print("\nМодель сохранена как 'laptop_price_model.pkl'.")
|
print("\nМодель сохранена как 'laptop_price_model.pkl'.")
|
||||||
|
|
||||||
# Дополнительно: Сохранение колонок, полученных после One-Hot Encoding, для использования в бэкенде
|
# Дополнительно: Сохранение колонок, полученных после One-Hot Encoding, для использования в бэкенде
|
||||||
feature_columns = X.columns.tolist()
|
feature_columns = X.columns.tolist()
|
||||||
joblib.dump(feature_columns, 'feature_columns.pkl')
|
joblib.dump(feature_columns, '../feature_columns.pkl')
|
||||||
print("Сохранены названия признаков в 'feature_columns.pkl'.")
|
print("Сохранены названия признаков в 'feature_columns.pkl'.")
|
40
services/service.py
Normal file
40
services/service.py
Normal file
@ -0,0 +1,40 @@
|
|||||||
|
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))
|
Loading…
Reference in New Issue
Block a user