Сравнить коммиты
13 Коммитов
3f4db4c51f
...
99d81722c4
| Автор | SHA1 | Дата |
|---|---|---|
|
|
99d81722c4 | 1 месяц назад |
|
|
6fe4557041 | 1 месяц назад |
|
|
667b6ac966 | 1 месяц назад |
|
|
434f4776b1 | 1 месяц назад |
|
|
296471990a | 1 месяц назад |
|
|
e232b693ca | 1 месяц назад |
|
|
9a6b531951 | 1 месяц назад |
|
|
aade830c20 | 1 месяц назад |
|
|
9780f6e710 | 1 месяц назад |
|
|
fd9a932e6c | 1 месяц назад |
|
|
91e7437e29 | 1 месяц назад |
|
|
b880d2d699 | 1 месяц назад |
|
|
813f622579 | 1 месяц назад |
@ -0,0 +1,169 @@
|
|||||||
|
# Сервис предсказания цен
|
||||||
|
|
||||||
|
Веб-сервис предсказания цен на подержанные автомобили. Мониторинг в комплекте.
|
||||||
|
|
||||||
|
Обзор сервисов (по `compose.yaml`, см. о развёртывании ниже):
|
||||||
|
|
||||||
|
| Профили Compose | Имя | Объекты | Описание |
|
||||||
|
|-----------------|------------------|------------------|------------------|
|
||||||
|
| — | `prices-predictor` | код: `ml_service/` | Веб-сервис предсказания цен, только stateless API. Об используемой предсказательной модели см. `research/README.md`. |
|
||||||
|
| — | `prometheus` | конфигурация: `prometheus/` | Мониторинг сервиса ([Prometheus](https://prometheus.io/)). |
|
||||||
|
| — | `grafana` | сохранённая конфигурация: `grafana/` | Аналитика и визуализация данных мониторига сервиса ([Grafana](https://grafana.com/)). |
|
||||||
|
| `with-testers` | `load-tester` | код: `load-tester/` | Генератор потока случайных запросов к `prices-predictor` для тестирования. |
|
||||||
|
|
||||||
|
Дополнительно:
|
||||||
|
|
||||||
|
* `models/` — расположение файла модели `model.pkl` для использования сервисом `prices-predictor`.
|
||||||
|
* `fetch_model_as_pickle_from_mlflow.py` — скрипт для экспорта предиктивной модели scikit-learn из MLFlow в файл.
|
||||||
|
|
||||||
|
## API сервиса предсказания цен
|
||||||
|
|
||||||
|
**Базовый URL**: `/api`. Все указанные далее URL записаны **относительно базового URL**, если не указано иное.
|
||||||
|
|
||||||
|
* Полная интерактивная документация (Swagger UI): `/docs`.
|
||||||
|
|
||||||
|
* Предсказать цену подержанного автомобиля: `/predict` (POST).
|
||||||
|
|
||||||
|
Пример запроса:
|
||||||
|
|
||||||
|
* requst query: `item_id=16` (параметр `item_id` необходим!);
|
||||||
|
|
||||||
|
* request body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"selling_price": 5.59,
|
||||||
|
"driven_kms": 27000.0,
|
||||||
|
"age": 5.0,
|
||||||
|
"fuel_type": "petrol",
|
||||||
|
"selling_type": "dealer",
|
||||||
|
"transmission_type": "manual"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* response body:
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"item_id": 16,
|
||||||
|
"price": 3.743508852258851
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
* Тестовый эндпоинт: `/` (GET).
|
||||||
|
|
||||||
|
Возвращает простой демонстрационный объект JSON.
|
||||||
|
|
||||||
|
Может использоваться для проверки состояния сервиса.
|
||||||
|
|
||||||
|
## Мониторинг
|
||||||
|
|
||||||
|
### Prometheus UI
|
||||||
|
|
||||||
|
#### Примеры запросов
|
||||||
|
|
||||||
|
Гистограмма предсказанных цен `model_prediction_value_bucket` (запрос: `rate(model_prediction_value_bucket[5m]`):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Гистограмма продолжительности предсказания цен моделью ML `model_prediction_seconds_bucket` (запрос: `rate(model_prediction_seconds_bucket[5m]`):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Интенсивность потока запросов к сервису предсказания цен с разными результатами (успех — коды HTTP `2xx`, ошибки со стороны клиента — коды HTTP `4xx`) `http_requests_total{handler="/predict"}` (запрос: `rate(http_requests_total{handler="/predict"}[5m]`):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Интенсивность потока запросов к **веб-серверу** сервиса предсказания цен **с ошибками** `http_requests_total{handler="/predict"}` (запрос: `sum without(handler, method) (rate(http_requests_total{status=~"4..|5.."}[5m]))`):
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
### Дашборд в Grafana
|
||||||
|
|
||||||
|
Дашборд экспортирован в файл: `grafana/objects/dashboard-1765200932880.json`.
|
||||||
|
|
||||||
|

|
||||||
|
|
||||||
|
Элементы:
|
||||||
|
|
||||||
|
* мониторинг модели:
|
||||||
|
* гистограмма распределения предсказанных цен за период времени (10 мин);
|
||||||
|
* прикладной уровень:
|
||||||
|
* интенсивность потока запросов (всех запросов; запросов, заканчивающихся ошибкой);
|
||||||
|
* инфраструктурный уровень:
|
||||||
|
* состояние сервиса (up/down);
|
||||||
|
* выделенный процессу объём VRAM.
|
||||||
|
|
||||||
|
## Развёртывание
|
||||||
|
|
||||||
|
### Файл модели
|
||||||
|
|
||||||
|
Файл используемой предсказательной модели можно извлечь из MLFlow скриптом `models/fetch_model_as_pickle_from_mlflow.py`. Файл модели можно размещается в `models/model.pkl`.
|
||||||
|
|
||||||
|
Например, извлечь модель по имени (`<model-name>`) и версии (`<model-version>`) (например, `UsedCardPricePredictionFinal/1`) (команда запускается из корневой директории проекта — от этого зависит путь к создаваемому файлу):
|
||||||
|
|
||||||
|
python services/models/fetch_model_as_pickle_from_mlflow.py --model "models:/<model-name>/<model-version>" services/models/model.pkl
|
||||||
|
|
||||||
|
Можно указать адрес tracking сервера MLFlow, например: `--tracking-uri "http://localhost:5000"`.
|
||||||
|
|
||||||
|
Информация о других опциях доступна:
|
||||||
|
|
||||||
|
python services/models/fetch_model_as_pickle_from_mlflow.py --help
|
||||||
|
|
||||||
|
### Образы Docker
|
||||||
|
|
||||||
|
#### `ml_model` (для сервиса `prices-predictor`)
|
||||||
|
|
||||||
|
**Сборка образа** (замените `<version>` на номер версии) (команда запускается из корневой директории проекта — от этого зависит путь к директории):
|
||||||
|
|
||||||
|
docker build -t ml_service:<version> services/ml_service/
|
||||||
|
|
||||||
|
**Независимый запуск** (замените `<version>` на номер версии образа, `<models-dir>` на **абсолютный** путь к директории, где размещён файл предсказательной модели `model.pkl`, `<port>` на порт для запуска веб-сервиса (например, `8000`)):
|
||||||
|
|
||||||
|
docker run -v "<models-dir>:/models" -p <port>:8000 ml_service:<version>
|
||||||
|
|
||||||
|
Модель может быть размещена в `models/`; тогда, например, при запуске команды из корна проекта: `$(pwd)/services/models` (здесь `$(pwd)` используется потому, что необходим абсолютный путь).
|
||||||
|
|
||||||
|
#### `load-tester` (для сервиса `load-tester`)
|
||||||
|
|
||||||
|
**Сборка образа** (замените `<version>` на номер версии) (команда запускается из корневой директории проекта — от этого зависит путь к директории):
|
||||||
|
|
||||||
|
docker build -t load_tester:<version> services/load_tester/
|
||||||
|
|
||||||
|
**Независимый запуск** (замените `<version>` на номер версии образа, `<api-base-url>` на базовый URL сервиса `prices-predictor` (например, `http://prices-predictor:8000/api`)):
|
||||||
|
|
||||||
|
docker run -e "API_BASE_URL=<api-base-url>" ml_service:<version>
|
||||||
|
|
||||||
|
### Развёртывание сервиса посредством Compose
|
||||||
|
|
||||||
|
Конфигурация описана в файле `compose.yaml`. Имя системы: `mpei-iis-system`.
|
||||||
|
|
||||||
|
Рекомендуется (не обязательно) использовать env-файл `compose.env`. Используйте файл `compose.env.template` как шаблон.
|
||||||
|
|
||||||
|
**Директория `models/` используется сервисом `prices-predictor` как том** с файлом модели `model.pkl`. Поместите туда файл модели, см. [Файл модели](#файл-модели).
|
||||||
|
|
||||||
|
**Управление сервисом с мониторингом** (замените `<command>` и `[options...]`):
|
||||||
|
|
||||||
|
docker compose -f services/compose.yaml --env-file services/compose.env <command> [options...]
|
||||||
|
|
||||||
|
**Для запуска вместе с генераторами тестовых запросов** используйте опцию compose `--profile=with-tester`.
|
||||||
|
|
||||||
|
Основные команды `docker compose`:
|
||||||
|
|
||||||
|
* `up`: создать и запустить контейнеры (также тома, сети и прочее); оставляет вывод логов прикреплённым к терминалу, `SIGINT` останавливает контейнеры, **но не удаляет созданные объекты**;
|
||||||
|
* опция `-d`: то же, но открепляет процесс от терминала.
|
||||||
|
* `down`: остановить и удалить контейнеры (также сети и прочее; для удаления томов используйте опцию `-v`).
|
||||||
|
* `start`: запустить существующие контейнеры.
|
||||||
|
* `stop`: остановить контейнеры.
|
||||||
|
* `restart`: перезапустить контейнеры.
|
||||||
|
|
||||||
|
**Открытые на хосте интерфейсы**:
|
||||||
|
|
||||||
|
* `localhost:8010`: Сервис `prices-predictor`. Базовый URL: `/api`.
|
||||||
|
* `localhost:9090`: UI Prometheus.
|
||||||
|
* `localhost:3000`: Grafana.
|
||||||
|
|
||||||
|
**Доступные на хосте тома**:
|
||||||
|
|
||||||
|
* `mpei-iis-system_prometheus-storage`: БД Prometheus.
|
||||||
|
* `mpei-iis-system_grafana-storage`: БД Grafana.
|
||||||
|
После Ширина: | Высота: | Размер: 164 KiB |
|
После Ширина: | Высота: | Размер: 87 KiB |
|
После Ширина: | Высота: | Размер: 78 KiB |
|
После Ширина: | Высота: | Размер: 115 KiB |
|
После Ширина: | Высота: | Размер: 99 KiB |
@ -0,0 +1 @@
|
|||||||
|
objects/*.json -text
|
||||||
@ -0,0 +1,543 @@
|
|||||||
|
{
|
||||||
|
"apiVersion": "dashboard.grafana.app/v2beta1",
|
||||||
|
"kind": "Dashboard",
|
||||||
|
"metadata": {
|
||||||
|
"name": "adcdfv7",
|
||||||
|
"generation": 9,
|
||||||
|
"creationTimestamp": "2025-12-08T12:51:53Z",
|
||||||
|
"labels": {},
|
||||||
|
"annotations": {}
|
||||||
|
},
|
||||||
|
"spec": {
|
||||||
|
"annotations": [
|
||||||
|
{
|
||||||
|
"kind": "AnnotationQuery",
|
||||||
|
"spec": {
|
||||||
|
"builtIn": true,
|
||||||
|
"enable": true,
|
||||||
|
"hide": true,
|
||||||
|
"iconColor": "rgba(0, 211, 255, 1)",
|
||||||
|
"name": "Annotations & Alerts",
|
||||||
|
"query": {
|
||||||
|
"group": "grafana",
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"spec": {},
|
||||||
|
"version": "v0"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"cursorSync": "Off",
|
||||||
|
"editable": true,
|
||||||
|
"elements": {
|
||||||
|
"panel-1": {
|
||||||
|
"kind": "Panel",
|
||||||
|
"spec": {
|
||||||
|
"data": {
|
||||||
|
"kind": "QueryGroup",
|
||||||
|
"spec": {
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"hidden": false,
|
||||||
|
"query": {
|
||||||
|
"group": "prometheus",
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"spec": {
|
||||||
|
"editorMode": "builder",
|
||||||
|
"exemplar": false,
|
||||||
|
"expr": "process_virtual_memory_bytes",
|
||||||
|
"instant": false,
|
||||||
|
"interval": "10s",
|
||||||
|
"legendFormat": "__auto",
|
||||||
|
"range": true
|
||||||
|
},
|
||||||
|
"version": "v0"
|
||||||
|
},
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"queryOptions": {},
|
||||||
|
"transformations": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "",
|
||||||
|
"id": 1,
|
||||||
|
"links": [],
|
||||||
|
"title": "Выделенный объём VRAM",
|
||||||
|
"vizConfig": {
|
||||||
|
"group": "timeseries",
|
||||||
|
"kind": "VizConfig",
|
||||||
|
"spec": {
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisBorderShow": false,
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"barWidthFactor": 0.6,
|
||||||
|
"drawStyle": "bars",
|
||||||
|
"fillOpacity": 0,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
},
|
||||||
|
"insertNulls": false,
|
||||||
|
"lineInterpolation": "stepAfter",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "auto",
|
||||||
|
"showValues": false,
|
||||||
|
"spanNulls": false,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
},
|
||||||
|
"thresholdsStyle": {
|
||||||
|
"mode": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": 0
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"color": "red",
|
||||||
|
"value": 80
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "bytes"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"hideZeros": false,
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": "12.4.0-20012734117"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"panel-2": {
|
||||||
|
"kind": "Panel",
|
||||||
|
"spec": {
|
||||||
|
"data": {
|
||||||
|
"kind": "QueryGroup",
|
||||||
|
"spec": {
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"hidden": false,
|
||||||
|
"query": {
|
||||||
|
"group": "prometheus",
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"spec": {
|
||||||
|
"editorMode": "builder",
|
||||||
|
"expr": "up",
|
||||||
|
"legendFormat": "__auto",
|
||||||
|
"range": true
|
||||||
|
},
|
||||||
|
"version": "v0"
|
||||||
|
},
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"queryOptions": {},
|
||||||
|
"transformations": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "",
|
||||||
|
"id": 2,
|
||||||
|
"links": [],
|
||||||
|
"title": "Состояние",
|
||||||
|
"vizConfig": {
|
||||||
|
"group": "state-timeline",
|
||||||
|
"kind": "VizConfig",
|
||||||
|
"spec": {
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "continuous-GrYlRd"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"fillOpacity": 70,
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
},
|
||||||
|
"insertNulls": false,
|
||||||
|
"lineWidth": 0,
|
||||||
|
"spanNulls": false
|
||||||
|
},
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "bool_on_off"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"alignValue": "left",
|
||||||
|
"legend": {
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"mergeValues": true,
|
||||||
|
"rowHeight": 0.9,
|
||||||
|
"showValue": "auto",
|
||||||
|
"tooltip": {
|
||||||
|
"hideZeros": false,
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": "12.4.0-20012734117"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"panel-3": {
|
||||||
|
"kind": "Panel",
|
||||||
|
"spec": {
|
||||||
|
"data": {
|
||||||
|
"kind": "QueryGroup",
|
||||||
|
"spec": {
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"hidden": false,
|
||||||
|
"query": {
|
||||||
|
"group": "prometheus",
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"spec": {
|
||||||
|
"editorMode": "builder",
|
||||||
|
"expr": "sum without(instance, method, status) (rate(http_requests_total{handler=\"/predict\"}[$__rate_interval]))",
|
||||||
|
"interval": "1m",
|
||||||
|
"legendFormat": "__auto",
|
||||||
|
"range": true
|
||||||
|
},
|
||||||
|
"version": "v0"
|
||||||
|
},
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"hidden": false,
|
||||||
|
"query": {
|
||||||
|
"group": "prometheus",
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"spec": {
|
||||||
|
"editorMode": "builder",
|
||||||
|
"expr": "rate(http_requests_total{handler=\"/predict\", status=~\"4..|5..\"}[$__rate_interval])",
|
||||||
|
"instant": false,
|
||||||
|
"interval": "1m",
|
||||||
|
"legendFormat": "__auto",
|
||||||
|
"range": true
|
||||||
|
},
|
||||||
|
"version": "v0"
|
||||||
|
},
|
||||||
|
"refId": "B"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"queryOptions": {},
|
||||||
|
"transformations": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "",
|
||||||
|
"id": 3,
|
||||||
|
"links": [],
|
||||||
|
"title": "HTTP-запросы",
|
||||||
|
"vizConfig": {
|
||||||
|
"group": "timeseries",
|
||||||
|
"kind": "VizConfig",
|
||||||
|
"spec": {
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "palette-classic"
|
||||||
|
},
|
||||||
|
"custom": {
|
||||||
|
"axisBorderShow": false,
|
||||||
|
"axisCenteredZero": false,
|
||||||
|
"axisColorMode": "text",
|
||||||
|
"axisLabel": "",
|
||||||
|
"axisPlacement": "auto",
|
||||||
|
"barAlignment": 0,
|
||||||
|
"barWidthFactor": 0.6,
|
||||||
|
"drawStyle": "line",
|
||||||
|
"fillOpacity": 0,
|
||||||
|
"gradientMode": "none",
|
||||||
|
"hideFrom": {
|
||||||
|
"legend": false,
|
||||||
|
"tooltip": false,
|
||||||
|
"viz": false
|
||||||
|
},
|
||||||
|
"insertNulls": false,
|
||||||
|
"lineInterpolation": "linear",
|
||||||
|
"lineWidth": 1,
|
||||||
|
"pointSize": 5,
|
||||||
|
"scaleDistribution": {
|
||||||
|
"type": "linear"
|
||||||
|
},
|
||||||
|
"showPoints": "auto",
|
||||||
|
"showValues": false,
|
||||||
|
"spanNulls": false,
|
||||||
|
"stacking": {
|
||||||
|
"group": "A",
|
||||||
|
"mode": "none"
|
||||||
|
},
|
||||||
|
"thresholdsStyle": {
|
||||||
|
"mode": "off"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "reqps"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": true
|
||||||
|
},
|
||||||
|
"tooltip": {
|
||||||
|
"hideZeros": false,
|
||||||
|
"mode": "single",
|
||||||
|
"sort": "none"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": "12.4.0-20012734117"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"panel-4": {
|
||||||
|
"kind": "Panel",
|
||||||
|
"spec": {
|
||||||
|
"data": {
|
||||||
|
"kind": "QueryGroup",
|
||||||
|
"spec": {
|
||||||
|
"queries": [
|
||||||
|
{
|
||||||
|
"kind": "PanelQuery",
|
||||||
|
"spec": {
|
||||||
|
"hidden": false,
|
||||||
|
"query": {
|
||||||
|
"group": "prometheus",
|
||||||
|
"kind": "DataQuery",
|
||||||
|
"spec": {
|
||||||
|
"editorMode": "builder",
|
||||||
|
"exemplar": false,
|
||||||
|
"expr": "sum without(instance) (increase(model_prediction_value_bucket[10m])) / on() group_left sum(increase(model_prediction_value_count[10m]))",
|
||||||
|
"format": "heatmap",
|
||||||
|
"instant": false,
|
||||||
|
"interval": "10m",
|
||||||
|
"legendFormat": "<{{le}}",
|
||||||
|
"range": true
|
||||||
|
},
|
||||||
|
"version": "v0"
|
||||||
|
},
|
||||||
|
"refId": "A"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"queryOptions": {},
|
||||||
|
"transformations": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": "Подпись под каждым столбцом обозначает его максимальное соответствующее значение",
|
||||||
|
"id": 4,
|
||||||
|
"links": [],
|
||||||
|
"title": "Предсказанные цены за 10 минут",
|
||||||
|
"vizConfig": {
|
||||||
|
"group": "bargauge",
|
||||||
|
"kind": "VizConfig",
|
||||||
|
"spec": {
|
||||||
|
"fieldConfig": {
|
||||||
|
"defaults": {
|
||||||
|
"color": {
|
||||||
|
"mode": "thresholds"
|
||||||
|
},
|
||||||
|
"thresholds": {
|
||||||
|
"mode": "absolute",
|
||||||
|
"steps": [
|
||||||
|
{
|
||||||
|
"color": "green",
|
||||||
|
"value": 0
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
"unit": "percentunit"
|
||||||
|
},
|
||||||
|
"overrides": []
|
||||||
|
},
|
||||||
|
"options": {
|
||||||
|
"displayMode": "gradient",
|
||||||
|
"legend": {
|
||||||
|
"calcs": [],
|
||||||
|
"displayMode": "list",
|
||||||
|
"placement": "bottom",
|
||||||
|
"showLegend": false
|
||||||
|
},
|
||||||
|
"maxVizHeight": 300,
|
||||||
|
"minVizHeight": 16,
|
||||||
|
"minVizWidth": 8,
|
||||||
|
"namePlacement": "auto",
|
||||||
|
"orientation": "auto",
|
||||||
|
"reduceOptions": {
|
||||||
|
"calcs": [
|
||||||
|
"lastNotNull"
|
||||||
|
],
|
||||||
|
"fields": "",
|
||||||
|
"values": false
|
||||||
|
},
|
||||||
|
"showUnfilled": false,
|
||||||
|
"sizing": "auto",
|
||||||
|
"valueMode": "color"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"version": "12.4.0-20012734117"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"layout": {
|
||||||
|
"kind": "GridLayout",
|
||||||
|
"spec": {
|
||||||
|
"items": [
|
||||||
|
{
|
||||||
|
"kind": "GridLayoutItem",
|
||||||
|
"spec": {
|
||||||
|
"element": {
|
||||||
|
"kind": "ElementReference",
|
||||||
|
"name": "panel-4"
|
||||||
|
},
|
||||||
|
"height": 8,
|
||||||
|
"width": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "GridLayoutItem",
|
||||||
|
"spec": {
|
||||||
|
"element": {
|
||||||
|
"kind": "ElementReference",
|
||||||
|
"name": "panel-2"
|
||||||
|
},
|
||||||
|
"height": 8,
|
||||||
|
"width": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 0
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "GridLayoutItem",
|
||||||
|
"spec": {
|
||||||
|
"element": {
|
||||||
|
"kind": "ElementReference",
|
||||||
|
"name": "panel-3"
|
||||||
|
},
|
||||||
|
"height": 8,
|
||||||
|
"width": 12,
|
||||||
|
"x": 0,
|
||||||
|
"y": 8
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"kind": "GridLayoutItem",
|
||||||
|
"spec": {
|
||||||
|
"element": {
|
||||||
|
"kind": "ElementReference",
|
||||||
|
"name": "panel-1"
|
||||||
|
},
|
||||||
|
"height": 8,
|
||||||
|
"width": 12,
|
||||||
|
"x": 12,
|
||||||
|
"y": 8
|
||||||
|
}
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"links": [],
|
||||||
|
"liveNow": false,
|
||||||
|
"preload": false,
|
||||||
|
"tags": [],
|
||||||
|
"timeSettings": {
|
||||||
|
"autoRefresh": "30s",
|
||||||
|
"autoRefreshIntervals": [
|
||||||
|
"5s",
|
||||||
|
"10s",
|
||||||
|
"30s",
|
||||||
|
"1m",
|
||||||
|
"5m",
|
||||||
|
"15m",
|
||||||
|
"30m",
|
||||||
|
"1h",
|
||||||
|
"2h",
|
||||||
|
"1d"
|
||||||
|
],
|
||||||
|
"fiscalYearStartMonth": 0,
|
||||||
|
"from": "now-1h",
|
||||||
|
"hideTimepicker": false,
|
||||||
|
"timezone": "browser",
|
||||||
|
"to": "now"
|
||||||
|
},
|
||||||
|
"title": "Сервис предсказания цен",
|
||||||
|
"variables": []
|
||||||
|
},
|
||||||
|
"status": {}
|
||||||
|
}
|
||||||
@ -1,71 +0,0 @@
|
|||||||
# Сервис предсказания цен
|
|
||||||
|
|
||||||
Веб-сервис предсказания цен на подержанные автомобили; только stateless API. Об используемой предсказательной модели см. `research/README.md`.
|
|
||||||
|
|
||||||
## API
|
|
||||||
|
|
||||||
**Базовый URL**: `/api`. Все указанные далее URL записаны **относительно базового URL**, если не указано иное.
|
|
||||||
|
|
||||||
* Полная интерактивная документация (Swagger UI): `/docs`.
|
|
||||||
|
|
||||||
* Предсказать цену подержанного автомобиля: `/predict` (POST).
|
|
||||||
|
|
||||||
Пример запроса:
|
|
||||||
|
|
||||||
* requst query: `item_id=16` (параметр `item_id` необходим!);
|
|
||||||
|
|
||||||
* request body:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"selling_price": 5.59,
|
|
||||||
"driven_kms": 27000.0,
|
|
||||||
"age": 5.0,
|
|
||||||
"fuel_type": "petrol",
|
|
||||||
"selling_type": "dealer",
|
|
||||||
"transmission_type": "manual"
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* response body:
|
|
||||||
|
|
||||||
```json
|
|
||||||
{
|
|
||||||
"item_id": 16,
|
|
||||||
"price": 3.743508852258851
|
|
||||||
}
|
|
||||||
```
|
|
||||||
|
|
||||||
* Тестовый эндпоинт: `/` (GET).
|
|
||||||
|
|
||||||
Возвращает простой демонстрационный объект JSON.
|
|
||||||
|
|
||||||
Может использоваться для проверки состояния сервиса.
|
|
||||||
|
|
||||||
## Развёртывание
|
|
||||||
|
|
||||||
### Файл модели
|
|
||||||
|
|
||||||
Файл используемой предсказательной модели `model.pkl` можно извлечь из MLFlow скриптом `services/models/fetch_model_as_pickle_from_mlflow.py`. Файл модели можно разместить в директории проекта, а именно в `services/models/`.
|
|
||||||
|
|
||||||
Например, извлечь модель по имени (`<model-name>`) и версии (`<model-version>`) (например, `UsedCardPricePredictionFinal/1`) (команда запускается из корневой директории проекта — от этого зависит путь к создаваемому файлу):
|
|
||||||
|
|
||||||
python services/models/fetch_model_as_pickle_from_mlflow.py --model "models:/<model-name>/<model-version>" services/models/model.pkl
|
|
||||||
|
|
||||||
Можно указать адрес tracking сервера MLFlow, например: `--tracking-uri "http://localhost:5000"`.
|
|
||||||
|
|
||||||
Информация о других опциях доступна:
|
|
||||||
|
|
||||||
python services/models/fetch_model_as_pickle_from_mlflow.py --help
|
|
||||||
|
|
||||||
### Образ Docker
|
|
||||||
|
|
||||||
Сборка образа (замените `<version>` на номер версии) (команда запускается из корневой директории проекта — от этого зависит путь к директории):
|
|
||||||
|
|
||||||
docker build -t ml_service:<version> services/ml_service/
|
|
||||||
|
|
||||||
Запуск образа (замените `<version>` на номер версии образа, `<models-dir>` на **абсолютный** путь к директории, где размещён файл предсказательной модели `model.pkl`, `<port>` на порт для запуска веб-сервиса (например, `8000`)):
|
|
||||||
|
|
||||||
docker run -v "<models-dir>:/models" -p <port>:8000 ml_service:<version>
|
|
||||||
|
|
||||||
Модель может быть размещена в директории проекта; тогда, например, при запуске команды из корна проекта: `$(pwd)/services/models` (здесь `$(pwd)` используется потому, что необходим абсолютный путь).
|
|
||||||
Загрузка…
Ссылка в новой задаче