# Информация о модели и метриках 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') # Функция загрузки модели @st.cache_data 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 # Функция загрузки скалера @st.cache_data 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(''' * Страница "Расчёт". На данной странице требуется ввести в ручном режиме: * Скорость шпинделя (об/мин) * Подача (мм/об) * Глубина резания (мм) * Длина обработки (мм) * Тип операции (категориальный) * Материал (категориальный) * Тип инструмента (категориальный) Далее требуется нажать кнопку "Рассчитать" -> Данный предикт модели будет рассчитан и автоматически сохранён (Его можно увидеть на странице "История"). * Страница "История". На данной странице можно просматривать результаты расчётов модели. Также имеется возможность построить график с целью определения параметров обработки деталей с минимальным временем обработки. При необходимости нажав на кнопку "Очистить историю" можно удалить все результаты (на данный момент отсутствует возможность фильтрации и соответственно удалении только определенных результатов) * Страница "О модели". Данная страница хранит информацию об используемой в приложении модели, параметрах обучения, графиков и т.д. ''')