feat(base.html): 优化页面样式与结构,提升移动端兼容性
This commit is contained in:
parent
d5daba182a
commit
e1efcc75a8
|
|
@ -1,256 +1,245 @@
|
|||
<!DOCTYPE html>
|
||||
<html lang="zh-CN">
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>{% block title %}AI模型训练监控看板{% endblock %}</title>
|
||||
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🧠</text></svg>" type="image/svg+xml">
|
||||
<link rel="alternate icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🧠</text></svg>" type="image/svg+xml">
|
||||
<link rel="stylesheet" href="{{ url_for('static', filename='css/bulma.css') }}">
|
||||
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
|
||||
<script src="{{ url_for('static', filename='js/plotly-2.27.0.min.js') }}" charset="utf-8"></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: 'SourceHanSans';
|
||||
src: url("{{ url_for('static', filename='fonts/SourceHanSansSC-Medium.otf') }}") format('opentype');
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'SmileySans';
|
||||
src: url("{{ url_for('static', filename='fonts/SmileySans-Oblique.ttf') }}") format('truetype');
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary-color: #00d1b2;
|
||||
--secondary-color: #3273dc;
|
||||
--success-color: #23d160;
|
||||
--warning-color: #ffdd57;
|
||||
--danger-color: #ff3860;
|
||||
--dark-color: #363636;
|
||||
--light-color: #f5f5f5;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: 'SourceHanSans', 'SmileySans', 'Segoe UI', 'Microsoft YaHei', sans-serif;
|
||||
background-color: #f8f9fa;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.card {
|
||||
border-radius: 12px;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||
border: none;
|
||||
transition: transform 0.3s ease, box-shadow 0.3s ease;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
transform: translateY(-5px);
|
||||
box-shadow: 0 8px 30px rgba(0,0,0,0.12);
|
||||
}
|
||||
|
||||
.card-header {
|
||||
background-color: transparent;
|
||||
border-bottom: 1px solid #e8e8e8;
|
||||
padding: 1.25rem 1.5rem;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
padding: 1.5rem;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
background: linear-gradient(135deg, #ffffff 0%, #f8f9fa 100%);
|
||||
border-left: 4px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
color: var(--dark-color);
|
||||
line-height: 1;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.metric-label {
|
||||
font-size: 0.875rem;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.metric-delta {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.positive-delta {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.negative-delta {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.status-active {
|
||||
background-color: var(--success-color);
|
||||
box-shadow: 0 0 10px var(--success-color);
|
||||
}
|
||||
|
||||
.status-inactive {
|
||||
background-color: var(--danger-color);
|
||||
box-shadow: 0 0 10px var(--danger-color);
|
||||
}
|
||||
|
||||
|
||||
.loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
.footer {
|
||||
background-color: var(--dark-color);
|
||||
color: white;
|
||||
padding: 2rem 1.5rem;
|
||||
margin-top: 3rem;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
<!doctype html>
|
||||
<html lang="zh-CN" data-theme="light">
|
||||
<head>
|
||||
<meta charset="UTF-8" />
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
||||
<title>{% block title %}AI模型训练监控看板{% endblock %}</title>
|
||||
<link
|
||||
rel="icon"
|
||||
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🧠</text></svg>"
|
||||
type="image/svg+xml"
|
||||
/>
|
||||
<link
|
||||
rel="alternate icon"
|
||||
href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🧠</text></svg>"
|
||||
type="image/svg+xml"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="{{ url_for('static', filename='css/bulma.css') }}"
|
||||
/>
|
||||
<link
|
||||
rel="stylesheet"
|
||||
href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"
|
||||
/>
|
||||
<script
|
||||
src="{{ url_for('static', filename='js/plotly-2.27.0.min.js') }}"
|
||||
charset="utf-8"
|
||||
></script>
|
||||
<style>
|
||||
@font-face {
|
||||
font-family: "SourceHanSans";
|
||||
src: url("{{ url_for('static', filename='fonts/SourceHanSansSC-Medium.otf') }}")
|
||||
format("opentype");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: "SmileySans";
|
||||
src: url("{{ url_for('static', filename='fonts/SmileySans-Oblique.ttf') }}")
|
||||
format("truetype");
|
||||
font-weight: normal;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
:root {
|
||||
--primary-color: #00d1b2;
|
||||
--secondary-color: #3273dc;
|
||||
--success-color: #23d160;
|
||||
--warning-color: #ffdd57;
|
||||
--danger-color: #ff3860;
|
||||
--dark-color: #363636;
|
||||
--light-color: #f5f5f5;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family:
|
||||
"SourceHanSans", "SmileySans", "Segoe UI",
|
||||
"Microsoft YaHei", sans-serif;
|
||||
min-height: 100vh;
|
||||
}
|
||||
|
||||
.metric-card {
|
||||
border-left: 4px solid var(--primary-color);
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: 2rem;
|
||||
font-size: 2.5rem;
|
||||
font-weight: 800;
|
||||
color: var(--dark-color);
|
||||
line-height: 1;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin-bottom: 1rem;
|
||||
|
||||
.metric-label {
|
||||
font-size: 0.875rem;
|
||||
color: #666;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
padding: 1rem;
|
||||
|
||||
.metric-delta {
|
||||
font-size: 0.875rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.control-panel {
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
padding: 1.5rem;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
</style>
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<nav class="navbar is-primary" role="navigation" aria-label="main navigation">
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="{{ url_for('index') }}">
|
||||
<span class="icon is-large">
|
||||
<i class="fas fa-chart-line fa-2x"></i>
|
||||
</span>
|
||||
<span class="title is-4 ml-2">AI模型训练监控看板</span>
|
||||
</a>
|
||||
|
||||
<a role="button" class="navbar-burger" aria-label="menu" aria-expanded="false" data-target="navbarMenu">
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarMenu" class="navbar-menu">
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<button class="button is-light">
|
||||
<span class="icon">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
</span>
|
||||
<span>刷新数据</span>
|
||||
</button>
|
||||
<a href="{{ url_for('api_status') }}" class="button is-info" target="_blank">
|
||||
<span class="icon">
|
||||
<i class="fas fa-code"></i>
|
||||
</span>
|
||||
<span>API接口</span>
|
||||
</a>
|
||||
|
||||
.positive-delta {
|
||||
color: var(--success-color);
|
||||
}
|
||||
|
||||
.negative-delta {
|
||||
color: var(--danger-color);
|
||||
}
|
||||
|
||||
.status-indicator {
|
||||
display: inline-block;
|
||||
width: 10px;
|
||||
height: 10px;
|
||||
border-radius: 50%;
|
||||
margin-right: 8px;
|
||||
}
|
||||
|
||||
.status-active {
|
||||
background-color: var(--success-color);
|
||||
box-shadow: 0 0 10px var(--success-color);
|
||||
}
|
||||
|
||||
.status-inactive {
|
||||
background-color: var(--danger-color);
|
||||
box-shadow: 0 0 10px var(--danger-color);
|
||||
}
|
||||
|
||||
.loading {
|
||||
opacity: 0.7;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.metric-value {
|
||||
font-size: 2rem;
|
||||
}
|
||||
}
|
||||
|
||||
.pagination .is-disabled,
|
||||
.pagination a[disabled],
|
||||
.pagination a.is-disabled {
|
||||
cursor: not-allowed;
|
||||
}
|
||||
</style>
|
||||
{% block extra_css %}{% endblock %}
|
||||
</head>
|
||||
<body>
|
||||
<nav
|
||||
class="navbar is-primary"
|
||||
role="navigation"
|
||||
aria-label="main navigation"
|
||||
>
|
||||
<div class="navbar-brand">
|
||||
<a class="navbar-item" href="{{ url_for('index') }}">
|
||||
<span class="icon is-large">
|
||||
<i class="fas fa-chart-line fa-2x"></i>
|
||||
</span>
|
||||
<span class="title is-4 ml-2">AI模型训练监控看板</span>
|
||||
</a>
|
||||
|
||||
<a
|
||||
role="button"
|
||||
class="navbar-burger"
|
||||
aria-label="menu"
|
||||
aria-expanded="false"
|
||||
data-target="navbarMenu"
|
||||
>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
<span aria-hidden="true"></span>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div id="navbarMenu" class="navbar-menu">
|
||||
<div class="navbar-end">
|
||||
<div class="navbar-item">
|
||||
<div class="buttons">
|
||||
<button class="button is-light" id="refreshBtn">
|
||||
<span class="icon">
|
||||
<i class="fas fa-sync-alt"></i>
|
||||
</span>
|
||||
<span>刷新数据</span>
|
||||
</button>
|
||||
<a
|
||||
href="{{ url_for('api_status') }}"
|
||||
class="button is-info"
|
||||
target="_blank"
|
||||
>
|
||||
<span class="icon">
|
||||
<i class="fas fa-code"></i>
|
||||
</span>
|
||||
<span>API接口</span>
|
||||
</a>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">
|
||||
{% block content %}{% endblock %}
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<footer class="footer">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
<strong>AI模型训练监控系统</strong> - 基于JSON旁路记录法的移动端友好监控方案
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
最后更新: <span id="lastUpdateTime">--:--:--</span> |
|
||||
数据源: <span id="dataSource">{{ data_source if data_source else '未设置' }}</span> |
|
||||
刷新间隔: <span id="refreshInterval">5</span>秒
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const navbarBurger = document.querySelector('.navbar-burger');
|
||||
const navbarMenu = document.querySelector('#navbarMenu');
|
||||
|
||||
if (navbarBurger) {
|
||||
navbarBurger.addEventListener('click', () => {
|
||||
navbarBurger.classList.toggle('is-active');
|
||||
navbarMenu.classList.toggle('is-active');
|
||||
});
|
||||
}
|
||||
|
||||
const refreshBtn = document.getElementById('refreshBtn');
|
||||
if (refreshBtn) {
|
||||
refreshBtn.addEventListener('click', () => {
|
||||
refreshBtn.classList.add('is-loading');
|
||||
setTimeout(() => {
|
||||
refreshBtn.classList.remove('is-loading');
|
||||
}, 1000);
|
||||
if (typeof window.refreshData === 'function') {
|
||||
window.refreshData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateTime() {
|
||||
const now = new Date();
|
||||
const timeStr = now.toLocaleTimeString('zh-CN');
|
||||
document.getElementById('lastUpdateTime').textContent = timeStr;
|
||||
}
|
||||
|
||||
updateTime();
|
||||
setInterval(updateTime, 1000);
|
||||
});
|
||||
</script>
|
||||
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</nav>
|
||||
|
||||
<section class="section">
|
||||
<div class="container">{% block content %}{% endblock %}</div>
|
||||
</section>
|
||||
|
||||
<footer class="footer has-background-dark has-text-white mt-5">
|
||||
<div class="content has-text-centered">
|
||||
<p>
|
||||
<strong>AI模型训练监控系统</strong> -
|
||||
基于JSON旁路记录法的移动端友好监控方案
|
||||
</p>
|
||||
<p class="mt-2">
|
||||
最后更新: <span id="lastUpdateTime">--:--:--</span> |
|
||||
数据源:
|
||||
<span id="dataSource"
|
||||
>{{ data_source if data_source else '未设置' }}</span
|
||||
>
|
||||
| 刷新间隔: <span id="refreshInterval">5</span>秒
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
|
||||
<script>
|
||||
document.addEventListener("DOMContentLoaded", function () {
|
||||
const navbarBurger = document.querySelector(".navbar-burger");
|
||||
const navbarMenu = document.querySelector("#navbarMenu");
|
||||
|
||||
if (navbarBurger) {
|
||||
navbarBurger.addEventListener("click", () => {
|
||||
navbarBurger.classList.toggle("is-active");
|
||||
navbarMenu.classList.toggle("is-active");
|
||||
});
|
||||
}
|
||||
|
||||
const refreshBtn = document.getElementById("refreshBtn");
|
||||
if (refreshBtn) {
|
||||
refreshBtn.addEventListener("click", () => {
|
||||
refreshBtn.classList.add("is-loading");
|
||||
setTimeout(() => {
|
||||
refreshBtn.classList.remove("is-loading");
|
||||
}, 1000);
|
||||
if (typeof window.refreshData === "function") {
|
||||
window.refreshData();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
function updateTime() {
|
||||
const now = new Date();
|
||||
const timeStr = now.toLocaleTimeString("zh-CN");
|
||||
document.getElementById("lastUpdateTime").textContent =
|
||||
timeStr;
|
||||
}
|
||||
|
||||
updateTime();
|
||||
setInterval(updateTime, 1000);
|
||||
});
|
||||
</script>
|
||||
|
||||
{% block extra_js %}{% endblock %}
|
||||
</body>
|
||||
</html>
|
||||
|
|
|
|||
|
|
@ -1,60 +1,116 @@
|
|||
{% extends "base.html" %}
|
||||
|
||||
{% block content %}
|
||||
<div class="control-panel">
|
||||
<div class="columns is-mobile is-vcentered">
|
||||
{% extends "base.html" %} {% block content %}
|
||||
<div class="box mb-5">
|
||||
<div class="columns is-desktop is-vcentered">
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<label class="label">数据源配置</label>
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select id="dataSourceSelect">
|
||||
<option value="local" {% if data_source_type == 'local' %}selected{% endif %}>本地文件</option>
|
||||
<option value="remote" {% if data_source_type == 'remote' %}selected{% endif %}>远程URL</option>
|
||||
<option
|
||||
value="local"
|
||||
{% if data_source_type=="local" %} selected {% endif %}
|
||||
>
|
||||
本地文件
|
||||
</option>
|
||||
<option
|
||||
value="remote"
|
||||
{% if data_source_type=="remote" %} selected {% endif %}
|
||||
>
|
||||
远程URL
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column">
|
||||
<div class="field" id="localFileField" {% if data_source_type == 'remote' %}style="display: none;"{% endif %}>
|
||||
<div
|
||||
class="field"
|
||||
id="localFileField"
|
||||
{% if data_source_type=="remote" %} style="display: none;"{% endif %}
|
||||
>
|
||||
<label class="label">本地文件路径</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" id="localFilePath" value="{{ data_source if data_source_type == 'local' else './output/training_status.json' }}" placeholder="./output/training_status.json">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
id="localFilePath"
|
||||
value="{{ data_source if data_source_type == 'local' else './output/training_status.json' }}"
|
||||
placeholder="./output/training_status.json"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="field" id="remoteUrlField" {% if data_source_type != 'remote' %}style="display: none;"{% endif %}>
|
||||
|
||||
<div
|
||||
class="field"
|
||||
id="remoteUrlField"
|
||||
{% if data_source_type !="remote" %} style="display: none;" {% endif %}
|
||||
>
|
||||
<label class="label">远程URL地址</label>
|
||||
<div class="control">
|
||||
<input class="input" type="text" id="remoteUrl" value="{{ data_source if data_source_type == 'remote' else '' }}" placeholder="http://服务器IP:端口/training_status.json">
|
||||
<input
|
||||
class="input"
|
||||
type="text"
|
||||
id="remoteUrl"
|
||||
value="{{ data_source if data_source_type == 'remote' else '' }}"
|
||||
placeholder="http://服务器IP:端口/training_status.json"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column">
|
||||
<div class="field">
|
||||
<label class="label">刷新间隔(秒)</label>
|
||||
<div class="control">
|
||||
<div class="select is-fullwidth">
|
||||
<select id="refreshIntervalSelect">
|
||||
<option value="1" {% if refresh_interval == 1 %}selected{% endif %}>1秒</option>
|
||||
<option value="2" {% if refresh_interval == 2 %}selected{% endif %}>2秒</option>
|
||||
<option value="5" {% if refresh_interval == 5 %}selected{% endif %}>5秒</option>
|
||||
<option value="10" {% if refresh_interval == 10 %}selected{% endif %}>10秒</option>
|
||||
<option value="30" {% if refresh_interval == 30 %}selected{% endif %}>30秒</option>
|
||||
<option
|
||||
value="1"
|
||||
{% if refresh_interval=="1" %} selected {% endif %}
|
||||
>
|
||||
1秒
|
||||
</option>
|
||||
<option
|
||||
value="2"
|
||||
{%if refresh_interval=="2"%} selected {% endif %}
|
||||
>
|
||||
2秒
|
||||
</option>
|
||||
<option
|
||||
value="5"
|
||||
{% if refresh_interval=="5" %} selected {% endif %}
|
||||
>
|
||||
5秒
|
||||
</option>
|
||||
<option
|
||||
value="10"
|
||||
{%if refresh_interval=="10"%}selected{%endif%}
|
||||
>
|
||||
10秒
|
||||
</option>
|
||||
<option
|
||||
value="30"
|
||||
{% if refresh_interval=="30" %} selected {% endif %}
|
||||
>
|
||||
30秒
|
||||
</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="column is-narrow">
|
||||
|
||||
<div class="column is-12-mobile is-narrow-desktop">
|
||||
<div class="field">
|
||||
<label class="label"> </label>
|
||||
<div class="control">
|
||||
<button id="applyConfigBtn" class="button is-primary is-fullwidth">
|
||||
<button
|
||||
id="applyConfigBtn"
|
||||
class="button is-primary is-fullwidth"
|
||||
>
|
||||
<span class="icon">
|
||||
<i class="fas fa-check"></i>
|
||||
</span>
|
||||
|
|
@ -78,7 +134,7 @@
|
|||
<div class="metric-delta" id="stepDelta"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-6-mobile is-3-tablet">
|
||||
<div class="has-text-centered">
|
||||
<div class="metric-value" id="currentEpoch">0</div>
|
||||
|
|
@ -86,23 +142,30 @@
|
|||
<div class="metric-delta" id="epochDelta"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-6-mobile is-3-tablet">
|
||||
<div class="has-text-centered">
|
||||
<div class="metric-value" id="trainLoss">0.0000</div>
|
||||
<div class="metric-value" id="trainLoss">
|
||||
0.0000
|
||||
</div>
|
||||
<div class="metric-label">训练损失</div>
|
||||
<div class="metric-delta" id="trainLossDelta"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-6-mobile is-3-tablet">
|
||||
<div class="has-text-centered">
|
||||
<div class="metric-value" id="trainAccuracy">0.0000</div>
|
||||
<div class="metric-value" id="trainAccuracy">
|
||||
0.0000
|
||||
</div>
|
||||
<div class="metric-label">训练准确率</div>
|
||||
<div class="metric-delta" id="trainAccuracyDelta"></div>
|
||||
<div
|
||||
class="metric-delta"
|
||||
id="trainAccuracyDelta"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-6-mobile is-3-tablet">
|
||||
<div class="has-text-centered">
|
||||
<div class="metric-value" id="evalLoss">0.0000</div>
|
||||
|
|
@ -110,23 +173,33 @@
|
|||
<div class="metric-delta" id="evalLossDelta"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-6-mobile is-3-tablet">
|
||||
<div class="has-text-centered">
|
||||
<div class="metric-value" id="evalAccuracy">0.0000</div>
|
||||
<div class="metric-value" id="evalAccuracy">
|
||||
0.0000
|
||||
</div>
|
||||
<div class="metric-label">评估准确率</div>
|
||||
<div class="metric-delta" id="evalAccuracyDelta"></div>
|
||||
<div
|
||||
class="metric-delta"
|
||||
id="evalAccuracyDelta"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-6-mobile is-3-tablet">
|
||||
<div class="has-text-centered">
|
||||
<div class="metric-value" id="learningRate">0.00e+0</div>
|
||||
<div class="metric-value" id="learningRate">
|
||||
0.00e+0
|
||||
</div>
|
||||
<div class="metric-label">学习率</div>
|
||||
<div class="metric-delta" id="learningRateDelta"></div>
|
||||
<div
|
||||
class="metric-delta"
|
||||
id="learningRateDelta"
|
||||
></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-6-mobile is-3-tablet">
|
||||
<div class="has-text-centered">
|
||||
<div class="metric-value" id="dataPoints">0</div>
|
||||
|
|
@ -138,39 +211,39 @@
|
|||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-12">
|
||||
<div class="columns is-multiline">
|
||||
<div class="column is-12-tablet is-6-desktop">
|
||||
<div class="chart-container">
|
||||
<div class="box">
|
||||
<h3 class="title is-5 mb-4">损失曲线</h3>
|
||||
<div id="lossChart"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-12-tablet is-6-desktop">
|
||||
<div class="chart-container">
|
||||
<div class="box">
|
||||
<h3 class="title is-5 mb-4">准确率曲线</h3>
|
||||
<div id="accuracyChart"></div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-12">
|
||||
<div class="chart-container">
|
||||
<div class="box">
|
||||
<h3 class="title is-5 mb-4">学习率变化</h3>
|
||||
<div id="learningRateChart"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div class="column is-12">
|
||||
<div class="card">
|
||||
<div class="card-header">
|
||||
<h3 class="title is-5">数据详情</h3>
|
||||
</div>
|
||||
<header class="card-header">
|
||||
<p class="card-header-title">数据详情</p>
|
||||
</header>
|
||||
<div class="card-content">
|
||||
<div class="table-container">
|
||||
<div class="table-container" style="max-height: 500px; overflow-y: auto;">
|
||||
<table class="table is-fullwidth is-striped is-hoverable">
|
||||
<thead>
|
||||
<tr>
|
||||
|
|
@ -186,35 +259,19 @@
|
|||
</thead>
|
||||
<tbody id="dataTableBody">
|
||||
<tr>
|
||||
<td colspan="8" class="has-text-centered">加载数据中...</td>
|
||||
<td colspan="8" class="has-text-centered">
|
||||
加载数据中...
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<nav class="pagination is-centered mt-4" role="navigation" aria-label="pagination">
|
||||
<a class="pagination-previous" id="prevPageBtn" disabled>上一页</a>
|
||||
<a class="pagination-next" id="nextPageBtn" disabled>下一页</a>
|
||||
<ul class="pagination-list">
|
||||
<li><span class="pagination-ellipsis">…</span></li>
|
||||
<li><span id="pageInfo" class="pagination-link is-current">第1页</span></li>
|
||||
<li><span class="pagination-ellipsis">…</span></li>
|
||||
</ul>
|
||||
<div class="field has-addons is-pulled-right">
|
||||
<div class="control">
|
||||
<input class="input" type="number" id="pageSizeInput" min="5" max="50" step="5" value="10" style="width: 80px;">
|
||||
</div>
|
||||
<div class="control">
|
||||
<a class="button is-static">条/页</a>
|
||||
</div>
|
||||
</div>
|
||||
</nav>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
|
||||
{% block extra_js %}
|
||||
{% endblock %} {% block extra_js %}
|
||||
<script>
|
||||
let currentData = [];
|
||||
let refreshTimer = null;
|
||||
|
|
@ -222,24 +279,15 @@
|
|||
let currentDataSourceType = '{{ data_source_type }}';
|
||||
let currentDataSource = '{{ data_source }}';
|
||||
let lastDataHash = '';
|
||||
let currentPage = 1;
|
||||
let pageSize = 10;
|
||||
let totalPages = 1;
|
||||
let prevPageBtn = null;
|
||||
let nextPageBtn = null;
|
||||
let pageInfo = null;
|
||||
|
||||
|
||||
document.addEventListener('DOMContentLoaded', function() {
|
||||
const dataSourceSelect = document.getElementById('dataSourceSelect');
|
||||
const localFileField = document.getElementById('localFileField');
|
||||
const remoteUrlField = document.getElementById('remoteUrlField');
|
||||
const refreshIntervalSelect = document.getElementById('refreshIntervalSelect');
|
||||
const applyConfigBtn = document.getElementById('applyConfigBtn');
|
||||
prevPageBtn = document.getElementById('prevPageBtn');
|
||||
nextPageBtn = document.getElementById('nextPageBtn');
|
||||
const pageSizeInput = document.getElementById('pageSizeInput');
|
||||
pageInfo = document.getElementById('pageInfo');
|
||||
|
||||
|
||||
|
||||
dataSourceSelect.addEventListener('change', function() {
|
||||
if (this.value === 'local') {
|
||||
localFileField.style.display = 'block';
|
||||
|
|
@ -249,11 +297,11 @@
|
|||
remoteUrlField.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
applyConfigBtn.addEventListener('click', function() {
|
||||
const dataSourceType = dataSourceSelect.value;
|
||||
let dataSource = '';
|
||||
|
||||
|
||||
if (dataSourceType === 'local') {
|
||||
dataSource = document.getElementById('localFilePath').value.trim();
|
||||
if (!dataSource) {
|
||||
|
|
@ -271,33 +319,32 @@
|
|||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const refreshInterval = parseInt(refreshIntervalSelect.value);
|
||||
|
||||
|
||||
currentDataSourceType = dataSourceType;
|
||||
currentDataSource = dataSource;
|
||||
currentRefreshInterval = refreshInterval;
|
||||
|
||||
|
||||
document.getElementById('refreshInterval').textContent = refreshInterval;
|
||||
document.getElementById('dataSource').textContent = dataSource;
|
||||
|
||||
|
||||
localStorage.setItem('monitor_config', JSON.stringify({
|
||||
dataSourceType: dataSourceType,
|
||||
dataSource: dataSource,
|
||||
refreshInterval: refreshInterval
|
||||
}));
|
||||
|
||||
|
||||
showNotification('配置已应用,正在重新加载数据...', 'success');
|
||||
|
||||
|
||||
// 重置数据状态,因为数据源已改变
|
||||
lastDataHash = '';
|
||||
currentPage = 1;
|
||||
|
||||
|
||||
clearInterval(refreshTimer);
|
||||
loadData();
|
||||
startRefreshTimer();
|
||||
});
|
||||
|
||||
|
||||
const savedConfig = localStorage.getItem('monitor_config');
|
||||
if (savedConfig) {
|
||||
try {
|
||||
|
|
@ -314,11 +361,11 @@
|
|||
remoteUrlField.style.display = 'block';
|
||||
}
|
||||
refreshIntervalSelect.value = config.refreshInterval || 5;
|
||||
|
||||
|
||||
currentDataSourceType = config.dataSourceType;
|
||||
currentDataSource = config.dataSource;
|
||||
currentRefreshInterval = config.refreshInterval || 5;
|
||||
|
||||
|
||||
document.getElementById('refreshInterval').textContent = config.refreshInterval || 5;
|
||||
document.getElementById('dataSource').textContent = config.dataSource;
|
||||
}
|
||||
|
|
@ -326,56 +373,22 @@
|
|||
console.error('加载配置失败:', e);
|
||||
}
|
||||
}
|
||||
|
||||
// 分页事件监听器
|
||||
if (prevPageBtn) {
|
||||
prevPageBtn.addEventListener('click', () => {
|
||||
if (currentPage > 1) {
|
||||
currentPage--;
|
||||
updateDataTable(currentData);
|
||||
updatePagination();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (nextPageBtn) {
|
||||
nextPageBtn.addEventListener('click', () => {
|
||||
if (currentPage < totalPages) {
|
||||
currentPage++;
|
||||
updateDataTable(currentData);
|
||||
updatePagination();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
if (pageSizeInput) {
|
||||
pageSizeInput.addEventListener('change', () => {
|
||||
const newPageSize = parseInt(pageSizeInput.value);
|
||||
if (newPageSize >= 5 && newPageSize <= 50) {
|
||||
pageSize = newPageSize;
|
||||
currentPage = 1;
|
||||
updateDataTable(currentData);
|
||||
updatePagination();
|
||||
} else {
|
||||
pageSizeInput.value = pageSize;
|
||||
showNotification('每页显示数量应在5到50之间', 'warning');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
function startRefreshTimer() {
|
||||
clearInterval(refreshTimer);
|
||||
refreshTimer = setInterval(loadData, currentRefreshInterval * 1000);
|
||||
}
|
||||
|
||||
|
||||
window.refreshData = loadData;
|
||||
|
||||
|
||||
loadData();
|
||||
startRefreshTimer();
|
||||
});
|
||||
|
||||
|
||||
function computeDataHash(data) {
|
||||
if (!data || data.length === 0) {
|
||||
return 'empty';
|
||||
|
|
@ -385,17 +398,17 @@
|
|||
const latestTimestamp = latest.timestamp || '';
|
||||
return `${data.length}:${latestStep}:${latestTimestamp}`;
|
||||
}
|
||||
|
||||
|
||||
function loadData() {
|
||||
const loadingElement = document.getElementById('dataTableBody');
|
||||
if (loadingElement) {
|
||||
loadingElement.innerHTML = '<tr><td colspan="8" class="has-text-centered">加载数据中...</td></tr>';
|
||||
}
|
||||
|
||||
|
||||
const params = new URLSearchParams();
|
||||
params.append('data_source_type', currentDataSourceType);
|
||||
params.append('data_source', currentDataSource);
|
||||
|
||||
|
||||
fetch(`/api/status?${params.toString()}`)
|
||||
.then(response => {
|
||||
if (!response.ok) {
|
||||
|
|
@ -409,9 +422,8 @@
|
|||
// 数据无变化,不刷新界面
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
lastDataHash = newHash;
|
||||
currentPage = 1; // 新数据到来,重置到第1页
|
||||
currentData = data;
|
||||
updateMetrics(data);
|
||||
updateCharts(data);
|
||||
|
|
@ -426,19 +438,19 @@
|
|||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function updateMetrics(data) {
|
||||
if (!data || data.length === 0) {
|
||||
resetMetrics();
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const latest = data[data.length - 1];
|
||||
const prev = data.length >= 2 ? data[data.length - 2] : null;
|
||||
|
||||
|
||||
function formatValue(value, format = 'number') {
|
||||
if (value === undefined || value === null) return 'N/A';
|
||||
|
||||
|
||||
if (format === 'number') {
|
||||
return Number(value).toLocaleString('zh-CN');
|
||||
} else if (format === 'float4') {
|
||||
|
|
@ -448,13 +460,13 @@
|
|||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
function updateMetric(elementId, value, delta = null, format = 'number') {
|
||||
const element = document.getElementById(elementId);
|
||||
if (element) {
|
||||
element.textContent = formatValue(value, format);
|
||||
}
|
||||
|
||||
|
||||
if (delta !== null) {
|
||||
const deltaElement = document.getElementById(elementId + 'Delta');
|
||||
if (deltaElement) {
|
||||
|
|
@ -468,7 +480,7 @@
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
updateMetric('currentStep', latest.step, prev ? latest.step - prev.step : null);
|
||||
updateMetric('currentEpoch', latest.epoch, prev ? latest.epoch - prev.epoch : null);
|
||||
updateMetric('trainLoss', latest['train/loss'], prev ? latest['train/loss'] - prev['train/loss'] : null, 'float4');
|
||||
|
|
@ -478,7 +490,7 @@
|
|||
updateMetric('learningRate', latest['train/learning_rate'], prev ? latest['train/learning_rate'] - prev['train/learning_rate'] : null, 'scientific');
|
||||
updateMetric('dataPoints', data.length, null);
|
||||
}
|
||||
|
||||
|
||||
function resetMetrics() {
|
||||
document.getElementById('currentStep').textContent = '0';
|
||||
document.getElementById('currentEpoch').textContent = '0';
|
||||
|
|
@ -488,11 +500,11 @@
|
|||
document.getElementById('evalAccuracy').textContent = '0.0000';
|
||||
document.getElementById('learningRate').textContent = '0.00e+0';
|
||||
document.getElementById('dataPoints').textContent = '0';
|
||||
|
||||
|
||||
const deltaElements = document.querySelectorAll('.metric-delta');
|
||||
deltaElements.forEach(el => el.textContent = '');
|
||||
}
|
||||
|
||||
|
||||
function updateCharts(data) {
|
||||
if (!data || data.length === 0) {
|
||||
createEmptyChart('lossChart', '损失曲线', '暂无数据');
|
||||
|
|
@ -500,9 +512,9 @@
|
|||
createEmptyChart('learningRateChart', '学习率变化', '暂无数据');
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
const steps = data.map(d => d.step);
|
||||
|
||||
|
||||
const lossTrace1 = {
|
||||
x: steps,
|
||||
y: data.map(d => d['train/loss']),
|
||||
|
|
@ -511,7 +523,7 @@
|
|||
line: { color: '#1f77b4', width: 2 },
|
||||
marker: { size: 4 }
|
||||
};
|
||||
|
||||
|
||||
const lossTrace2 = {
|
||||
x: steps,
|
||||
y: data.map(d => d['eval/loss']),
|
||||
|
|
@ -520,7 +532,7 @@
|
|||
line: { color: '#ff7f0e', width: 2, dash: 'dash' },
|
||||
marker: { size: 4 }
|
||||
};
|
||||
|
||||
|
||||
Plotly.newPlot('lossChart', [lossTrace1, lossTrace2], {
|
||||
title: '损失曲线',
|
||||
xaxis: { title: '训练步数' },
|
||||
|
|
@ -530,7 +542,7 @@
|
|||
margin: { l: 50, r: 30, t: 50, b: 50 },
|
||||
legend: { orientation: 'h', y: 1.1 }
|
||||
});
|
||||
|
||||
|
||||
const accuracyTrace1 = {
|
||||
x: steps,
|
||||
y: data.map(d => d['train/accuracy']),
|
||||
|
|
@ -539,7 +551,7 @@
|
|||
line: { color: '#2ca02c', width: 2 },
|
||||
marker: { size: 4 }
|
||||
};
|
||||
|
||||
|
||||
const accuracyTrace2 = {
|
||||
x: steps,
|
||||
y: data.map(d => d['eval/accuracy']),
|
||||
|
|
@ -548,7 +560,7 @@
|
|||
line: { color: '#d62728', width: 2, dash: 'dash' },
|
||||
marker: { size: 4 }
|
||||
};
|
||||
|
||||
|
||||
Plotly.newPlot('accuracyChart', [accuracyTrace1, accuracyTrace2], {
|
||||
title: '准确率曲线',
|
||||
xaxis: { title: '训练步数' },
|
||||
|
|
@ -558,7 +570,7 @@
|
|||
margin: { l: 50, r: 30, t: 50, b: 50 },
|
||||
legend: { orientation: 'h', y: 1.1 }
|
||||
});
|
||||
|
||||
|
||||
if (data[0]['train/learning_rate'] !== undefined) {
|
||||
const lrTrace = {
|
||||
x: steps,
|
||||
|
|
@ -568,7 +580,7 @@
|
|||
line: { color: '#9467bd', width: 2 },
|
||||
marker: { size: 4 }
|
||||
};
|
||||
|
||||
|
||||
Plotly.newPlot('learningRateChart', [lrTrace], {
|
||||
title: '学习率变化',
|
||||
xaxis: { title: '训练步数' },
|
||||
|
|
@ -581,7 +593,7 @@
|
|||
createEmptyChart('learningRateChart', '学习率变化', '暂无学习率数据');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
function createEmptyChart(containerId, title, message) {
|
||||
const trace = {
|
||||
x: [0],
|
||||
|
|
@ -590,7 +602,7 @@
|
|||
marker: { size: 0 },
|
||||
showlegend: false
|
||||
};
|
||||
|
||||
|
||||
Plotly.newPlot(containerId, [trace], {
|
||||
title: title,
|
||||
xaxis: { showgrid: false, zeroline: false, showticklabels: false },
|
||||
|
|
@ -607,38 +619,25 @@
|
|||
margin: { l: 50, r: 30, t: 50, b: 50 }
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
function updateDataTable(data) {
|
||||
const tbody = document.getElementById('dataTableBody');
|
||||
if (!tbody) return;
|
||||
|
||||
|
||||
if (!data || data.length === 0) {
|
||||
tbody.innerHTML = '<tr><td colspan="8" class="has-text-centered">暂无数据</td></tr>';
|
||||
updatePagination();
|
||||
return;
|
||||
}
|
||||
|
||||
// 计算分页数据
|
||||
const totalItems = data.length;
|
||||
totalPages = Math.max(1, Math.ceil(totalItems / pageSize));
|
||||
|
||||
// 确保当前页在有效范围内
|
||||
if (currentPage > totalPages) currentPage = totalPages;
|
||||
if (currentPage < 1) currentPage = 1;
|
||||
|
||||
// 计算要显示的数据范围(从最新数据开始分页)
|
||||
const startIndex = Math.max(0, totalItems - currentPage * pageSize);
|
||||
const endIndex = Math.max(0, totalItems - (currentPage - 1) * pageSize);
|
||||
|
||||
// 获取数据并反转顺序(最新的数据在表格顶部)
|
||||
const pageData = data.slice(startIndex, endIndex).reverse();
|
||||
|
||||
|
||||
// 反转数据顺序,最新的数据显示在表格顶部
|
||||
const reversedData = data.slice().reverse();
|
||||
|
||||
let html = '';
|
||||
pageData.forEach(record => {
|
||||
const timestamp = record.timestamp ?
|
||||
new Date(record.timestamp).toLocaleString('zh-CN') :
|
||||
reversedData.forEach(record => {
|
||||
const timestamp = record.timestamp ?
|
||||
new Date(record.timestamp).toLocaleString('zh-CN') :
|
||||
'N/A';
|
||||
|
||||
|
||||
html += `
|
||||
<tr>
|
||||
<td>${record.step || 'N/A'}</td>
|
||||
|
|
@ -652,40 +651,17 @@
|
|||
</tr>
|
||||
`;
|
||||
});
|
||||
|
||||
|
||||
tbody.innerHTML = html;
|
||||
updatePagination();
|
||||
}
|
||||
|
||||
function updatePagination() {
|
||||
if (!prevPageBtn || !nextPageBtn || !pageInfo) return;
|
||||
|
||||
// 更新按钮状态
|
||||
prevPageBtn.disabled = currentPage <= 1;
|
||||
nextPageBtn.disabled = currentPage >= totalPages;
|
||||
|
||||
// 更新页数信息
|
||||
pageInfo.textContent = `第${currentPage}页 / 共${totalPages}页`;
|
||||
|
||||
// 更新按钮样式
|
||||
if (prevPageBtn.disabled) {
|
||||
prevPageBtn.classList.add('is-disabled');
|
||||
} else {
|
||||
prevPageBtn.classList.remove('is-disabled');
|
||||
}
|
||||
|
||||
if (nextPageBtn.disabled) {
|
||||
nextPageBtn.classList.add('is-disabled');
|
||||
} else {
|
||||
nextPageBtn.classList.remove('is-disabled');
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
function showNotification(message, type = 'info') {
|
||||
const notification = document.createElement('div');
|
||||
notification.className = `notification is-${type}`;
|
||||
notification.style.cssText = 'position: fixed; bottom: 20px; right: 20px; z-index: 1000; max-width: 350px; transform: translateX(120%); opacity: 0; transition: transform 0.4s cubic-bezier(0.68, -0.55, 0.27, 1.55), opacity 0.4s ease;';
|
||||
|
||||
|
||||
notification.innerHTML = `<button class="delete"></button>${message}`;
|
||||
notification.querySelector('.delete').addEventListener('click', () => {
|
||||
notification.style.transform = 'translateX(120%)';
|
||||
|
|
@ -696,15 +672,15 @@
|
|||
}
|
||||
}, 400);
|
||||
});
|
||||
|
||||
|
||||
document.body.appendChild(notification);
|
||||
|
||||
|
||||
// 触发滑动动画
|
||||
setTimeout(() => {
|
||||
notification.style.transform = 'translateX(0)';
|
||||
notification.style.opacity = '1';
|
||||
}, 10);
|
||||
|
||||
|
||||
setTimeout(() => {
|
||||
if (notification.parentNode) {
|
||||
notification.style.transform = 'translateX(120%)';
|
||||
|
|
@ -718,4 +694,4 @@
|
|||
}, 3000);
|
||||
}
|
||||
</script>
|
||||
{% endblock %}
|
||||
{% endblock %}
|
||||
|
|
|
|||
|
|
@ -1232,25 +1232,10 @@ def expand_and_train(
|
|||
num_experts: int = typer.Option(20, "--num-experts", help="MoE专家数量"),
|
||||
max_seq_len: int = typer.Option(128, "--max-seq-len", help="最大序列长度"),
|
||||
use_pinyin: bool = typer.Option(False, "--use-pinyin", help="是否使用拼音特征"),
|
||||
# 两阶段训练参数
|
||||
frozen_patience: int = typer.Option(
|
||||
10,
|
||||
"--frozen-patience",
|
||||
help="冻结阶段验证损失连续不下降的epoch数,触发切换到全量微调",
|
||||
),
|
||||
frozen_lr: float = typer.Option(1e-3, "--frozen-lr", help="冻结阶段学习率"),
|
||||
full_lr: float = typer.Option(1e-4, "--full-lr", help="全量微调阶段学习率"),
|
||||
frozen_scheduler: str = typer.Option(
|
||||
"cosine", "--frozen-scheduler", help="冻结阶段学习率调度器类型:cosine或plateau"
|
||||
),
|
||||
full_scheduler: str = typer.Option(
|
||||
"cosine",
|
||||
"--full-scheduler",
|
||||
help="全量微调阶段学习率调度器类型:cosine或plateau",
|
||||
),
|
||||
# 训练参数
|
||||
batch_size: int = typer.Option(128, "--batch-size", "-b", help="批次大小"),
|
||||
num_epochs: int = typer.Option(10, "--num-epochs", help="训练轮数"),
|
||||
learning_rate: float = typer.Option(1e-5, "--learning-rate", "-lr", help="学习率"),
|
||||
min_learning_rate: float = typer.Option(
|
||||
1e-9, "--min-learning-rate", help="最小学习率"
|
||||
),
|
||||
|
|
|
|||
Loading…
Reference in New Issue