Spaces:
Running
Running
| # Информация о модели и метриках | |
| MODEL_INFO = { | |
| 'type': 'Регрессионная модель', | |
| 'algorithm': 'LGBMRegressor', | |
| 'features': [ | |
| 'Скорость_шпинделя', | |
| 'Подача', | |
| 'Глубина_резания', | |
| 'Длина_обработки', | |
| 'Операция', | |
| 'Материал', | |
| 'Тип_инструмента' | |
| ], | |
| 'target': 'Время обработки (секунды)' | |
| } | |
| METRICS = { | |
| 'MSE': 379.5, | |
| 'RMSE': 19.4, | |
| 'MAE': 10.6, | |
| 'MAPE': 1.08, | |
| 'R2': 0.85 | |
| } | |
| import streamlit as st | |
| import pandas as pd | |
| import matplotlib.pyplot as plt | |
| import pickle | |
| import os | |
| import seaborn as sns | |
| from matplotlib import gridspec | |
| from datetime import datetime | |
| # Инициализация session_state для хранения результатов | |
| if 'results' not in st.session_state: | |
| st.session_state.results = [] | |
| # Создаем структуру папок | |
| if not os.path.exists('pages'): | |
| os.makedirs('pages') | |
| # Функция загрузки модели | |
| def load_model(model_path): | |
| if os.path.exists(model_path): | |
| with open(model_path, 'rb') as f: | |
| return pickle.load(f) | |
| else: | |
| st.error(f"Файл модели {model_path} не найден!") | |
| return None | |
| # Функция загрузки скалера | |
| def load_scaler(scaler_path): | |
| if os.path.exists(scaler_path): | |
| with open(scaler_path, 'rb') as f: | |
| return pickle.load(f) | |
| else: | |
| st.error(f"Файл скалера {scaler_path} не найден!") | |
| return None | |
| # Функция сохранения результатов | |
| def save_result(result): | |
| st.session_state.results.append(result) # Добавляем результат без сброса истории | |
| # Функция очистки истории | |
| def clear_results(): | |
| st.session_state.results = [] | |
| # Функция визуализации зависимостей | |
| def plot_dependencies(df): | |
| fig = plt.figure(figsize=(15, 12)) | |
| gs = gridspec.GridSpec(2, 2) | |
| # График зависимости от скорости шпинделя | |
| ax1 = fig.add_subplot(gs[0, 0]) | |
| sns.scatterplot(data=df, x='Скорость_шпинделя', y='Время_обработки', ax=ax1, alpha=0.6) | |
| ax1.legend() | |
| ax1.set_title('Зависимость от скорости шпинделя') | |
| # График зависимости от подачи | |
| ax2 = fig.add_subplot(gs[0, 1]) | |
| sns.scatterplot(data=df, x='Подача', y='Время_обработки', ax=ax2, alpha=0.6) | |
| ax2.set_title('Зависимость от подачи') | |
| # График зависимости от глубины резания | |
| ax3 = fig.add_subplot(gs[1, 0]) | |
| sns.scatterplot(data=df, x='Глубина_резания', y='Время_обработки', ax=ax3, alpha=0.6) | |
| ax3.set_title('Зависимость от глубины резания') | |
| # График зависимости от длины обработки | |
| ax4 = fig.add_subplot(gs[1, 1]) | |
| sns.scatterplot(data=df, x='Длина_обработки', y='Время_обработки', ax=ax4, alpha=0.6) | |
| ax4.set_title('Зависимость от длины обработки') | |
| plt.tight_layout() | |
| st.pyplot(fig) | |
| df = pd.read_csv('df.csv') | |
| # Общие настройки приложения | |
| st.set_page_config( | |
| page_title='Прогноз времени обработки', | |
| layout='wide', | |
| initial_sidebar_state='auto', | |
| page_icon='⚙️' | |
| ) | |
| # Создание навигационного меню | |
| st.sidebar.title('Навигация') | |
| page = st.sidebar.radio( | |
| "Выберите страницу", | |
| ('Главная', 'Расчет', 'История', 'О модели','Инструкция') | |
| ) | |
| # Страница 1: Главная | |
| if page == 'Главная': | |
| st.title('Добро пожаловать в приложение - "Прогноз времени обработки"') | |
| st.markdown(''' | |
| Это веб-приложение поможет вам рассчитать время обработки деталей на станках при различных входных параметрах. | |
| ''') | |
| try: | |
| st.image('Main.jpg', width=1000) | |
| except Exception: | |
| st.warning("Изображение Main.jpg не найдено") | |
| # Страница 2: Расчет | |
| elif page == 'Расчет': | |
| st.title('Расчет времени обработки') | |
| # Загрузка модели и скалера | |
| model = load_model('model.pkl') | |
| scaler = load_scaler('scaler.pkl') | |
| if model is None or scaler is None: | |
| st.stop() | |
| # Словарь признаков | |
| features = dict( | |
| speed='Скорость_шпинделя', | |
| feed='Подача', | |
| depth='Глубина_резания', | |
| length='Длина_обработки', | |
| operation='Тип_операции', | |
| material='Тип_материала', | |
| tool='Тип_инструмента' | |
| ) | |
| # Слайдеры и радиокнопки | |
| speed = st.sidebar.number_input( | |
| label=f"{features['speed']} (об/мин)", | |
| min_value=245, | |
| max_value=3270, | |
| value=245, | |
| step=1, | |
| format='%d' | |
| ) | |
| feed = st.sidebar.number_input( | |
| label=f"{features['feed']} (мм/об)", | |
| min_value=0.007, | |
| max_value=0.538, | |
| value=0.007, | |
| step=0.001, | |
| format='%.3f' | |
| ) | |
| depth = st.sidebar.number_input( | |
| label=f"{features['depth']} (мм)", | |
| min_value=0.001, | |
| max_value=3.217, | |
| value=0.001, | |
| step=0.001, | |
| format='%.3f' | |
| ) | |
| length = st.sidebar.number_input( | |
| label=f"{features['length']} (мм)", | |
| min_value=1.0, | |
| max_value=324.0, | |
| value=1.0, | |
| step=1.0, | |
| format='%.0f' | |
| ) | |
| operations = st.sidebar.radio(features['operation'], ['Фрезерование', 'Точение'], horizontal=True) | |
| materials = st.sidebar.radio(features['material'], ['Латунь', 'Чугун', 'Низкоуглеродистая_сталь'], horizontal=True) | |
| tools = st.sidebar.radio(features['tool'], ['Твердосплавный', 'Твердосплавный_с_покрытием', 'Быстрорежущий'], horizontal=True) | |
| # Подготовка данных | |
| operation_to_index = {'Фрезерование': 0, 'Точение': 1} | |
| material_to_index = {'Латунь': 0, 'Чугун': 1, 'Низкоуглеродистая_сталь': 2} | |
| tool_to_index = {'Твердосплавный': 0, 'Твердосплавный_с_покрытием': 1, 'Быстрорежущий': 2} | |
| if operations == 'Фрезерование': | |
| try: | |
| st.image('Frez.jpg', width=600) | |
| except Exception: | |
| st.warning("Изображение Frez.jpg не найдено") | |
| elif operations == 'Точение': | |
| try: | |
| st.image('Toch.jpg', width=600) | |
| except Exception: | |
| st.warning("Изображение Toch.jpg не найдено") | |
| data_df = pd.DataFrame([dict( | |
| Скорость_шпинделя=speed, | |
| Подача=feed, | |
| Глубина_резания=depth, | |
| Длина_обработки=length, | |
| Операция=operation_to_index[operations], | |
| Материал=material_to_index[materials], | |
| Тип_инструмента=tool_to_index[tools] | |
| )]) | |
| # Вывод входных данных | |
| st.write("##### Ваши данные") | |
| st.write(data_df) | |
| # Расчет | |
| if st.button("Рассчитать"): | |
| try: | |
| s_data_df = scaler.transform(data_df) | |
| time_prob = model.predict(s_data_df) | |
| st.write("## Результаты расчета") | |
| st.write(f"Время обработки: {time_prob[0]:.2f} секунд") | |
| # Сохранение результата сразу после расчёта | |
| save_result({ | |
| 'timestamp': datetime.now(), | |
| 'data': data_df.to_dict(), | |
| 'time': time_prob[0] | |
| }) | |
| st.success('Результат успешно сохранен!') | |
| except Exception as e: | |
| st.error(f"Ошибка при расчёте: {str(e)}") | |
| # Страница 3: История | |
| elif page == 'История': | |
| st.title('История расчетов') | |
| if not st.session_state.results: | |
| st.info('История расчетов пуста') | |
| else: | |
| # Создаем DataFrame из сохраненных результатов | |
| results_df = pd.DataFrame([ | |
| { | |
| 'Номер': idx + 1, | |
| 'Дата': result['timestamp'].strftime('%d.%m.%Y %H:%M'), | |
| 'Скорость шпинделя, (об/мин)': result['data']['Скорость_шпинделя'][0], | |
| 'Подача, (мм/об)': result['data']['Подача'][0], | |
| 'Глубина резания, (мм)': result['data']['Глубина_резания'][0], | |
| 'Длина обработки, (мм)': result['data']['Длина_обработки'][0], | |
| 'Тип операции': ['Фрезерование', 'Точение'][result['data']['Операция'][0]], | |
| 'Материал': ['Латунь', 'Чугун', 'Низкоуглеродистая_сталь'][result['data']['Материал'][0]], | |
| 'Инструмент': ['Твердосплавный', 'Твердосплавный_с_покрытием', 'Быстрорежущий'][result['data']['Тип_инструмента'][0]], | |
| 'Время обработки, (сек)': result['time'] | |
| } | |
| for idx,result in enumerate(st.session_state.results) | |
| ]) | |
| # Отображаем таблицу | |
| st.dataframe(results_df) | |
| # Кнопка для построения графика | |
| if st.button('Построить график истории'): | |
| try: | |
| # Создаем график | |
| fig, ax = plt.subplots(figsize=(12, 6)) | |
| plt.plot( | |
| results_df['Номер'], | |
| results_df['Время обработки, (сек)'], | |
| marker='o', | |
| linestyle='-', | |
| color='blue' | |
| ) | |
| # Находим минимальное значение | |
| min_time = results_df['Время обработки, (сек)'].min() | |
| min_index = results_df[results_df['Время обработки, (сек)'] == min_time]['Номер'].values[0] | |
| # Отмечаем минимальное значение | |
| plt.plot( | |
| min_index, | |
| min_time, | |
| marker='o', | |
| color='red', | |
| markersize=10 | |
| ) | |
| plt.text( | |
| min_index, | |
| min_time, | |
| f'Минимум: {min_time:.2f} сек', | |
| verticalalignment='bottom', | |
| horizontalalignment='center' | |
| ) | |
| # Форматируем оси | |
| plt.title('Динамика времени обработки по истории расчетов') | |
| plt.xlabel('Номер расчета') | |
| plt.ylabel('Время обработки (сек)') | |
| plt.grid(True) | |
| plt.xticks(results_df['Номер']) # Показываем все номера | |
| plt.tight_layout() | |
| st.pyplot(fig) | |
| except Exception as e: | |
| st.error(f"Ошибка при построении графика: {str(e)}") | |
| # Добавляем кнопку очистки истории | |
| if st.button('Очистить историю'): | |
| clear_results() | |
| st.success('История успешно очищена') | |
| # Страница 4: Информация о модели | |
| elif page == 'О модели': | |
| st.title('О модели прогнозирования') | |
| st.subheader('Информация о модели') | |
| st.write(f"Тип модели: {MODEL_INFO['type']}") | |
| st.write(f"Используемый алгоритм: {MODEL_INFO['algorithm']}") | |
| st.subheader('Входные параметры') | |
| st.write(MODEL_INFO['features']) | |
| st.subheader('Целевая переменная') | |
| st.write(MODEL_INFO['target']) | |
| st.subheader('Метрики качества на тестовой выборке') | |
| metrics_df = pd.DataFrame(METRICS.items(), columns=['Метрика', 'Значение']) | |
| st.dataframe(metrics_df) | |
| st.subheader('Важные характеристики') | |
| st.markdown(''' | |
| * Модель обучена на наборе данных с 47553 наблюдений | |
| * Данные прошли предварительную обработку (EDA) | |
| * Использовано масштабирование признаков | |
| * Модель показывает точность прогнозирования на тестовых данных ~85% | |
| # Далее представлены графики зависимости времени обработки от некоторых признаков, а также карта корреляций | |
| ''') | |
| plot_dependencies(df) | |
| st.subheader('Корреляция параметров') | |
| fig_corr = plt.figure(figsize=(5, 4)) | |
| sns.heatmap(df.corr(), annot=True, cmap='coolwarm', fmt=".2f") | |
| st.pyplot(fig_corr) | |
| # Страница 5: Инструкция | |
| elif page == 'Инструкция': | |
| st.title('Инструкция по работе с приложением') | |
| st.markdown(''' | |
| * Страница "Расчёт". На данной странице требуется ввести в ручном режиме: | |
| * Скорость шпинделя (об/мин) | |
| * Подача (мм/об) | |
| * Глубина резания (мм) | |
| * Длина обработки (мм) | |
| * Тип операции (категориальный) | |
| * Материал (категориальный) | |
| * Тип инструмента (категориальный) | |
| Далее требуется нажать кнопку "Рассчитать" -> Данный предикт модели будет рассчитан и автоматически сохранён (Его можно увидеть на странице "История"). | |
| * Страница "История". На данной странице можно просматривать результаты расчётов модели. | |
| Также имеется возможность построить график с целью определения параметров обработки деталей с минимальным временем обработки. | |
| При необходимости нажав на кнопку "Очистить историю" можно удалить все результаты (на данный момент отсутствует возможность фильтрации и соответственно удалении только определенных результатов) | |
| * Страница "О модели". Данная страница хранит информацию об используемой в приложении модели, параметрах обучения, графиков и т.д. | |
| ''') | |