Browse Source

Merge branch 'master' into dev-xjf

pull/83/head
xjf 4 weeks ago
parent
commit
21be2f42de
  1. 6
      src/api/alert/model/models.ts
  2. 14
      src/views/model/AssessReport.vue
  3. 22
      src/views/model/list/step/Step2.vue
  4. 12
      src/views/model/list/step/Step3.vue
  5. 10
      src/views/model/list/step/data.tsx
  6. 6
      src/views/model/train/data.tsx
  7. 208
      src/views/model/train/index.vue

6
src/api/alert/model/models.ts

@ -10,7 +10,9 @@ enum Api {
CALCULATE_BACK = '/alert/model/data/calculate/', CALCULATE_BACK = '/alert/model/data/calculate/',
OPTIMISTIC = '/alert/optimistic', OPTIMISTIC = '/alert/optimistic',
TRAIN_MODEL = '/alert/model/train', TRAIN_MODEL = '/alert/model/train',
TRAIN_MODEL_ANN = '/alert/model/train/ann',
TEST_MODEL = '/alert/model/test', TEST_MODEL = '/alert/model/test',
TEST_MODEL_ANN = '/alert/model/test/ann',
BOTTOM_MODEL = '/alert/model/bottom/', BOTTOM_MODEL = '/alert/model/bottom/',
VERSION_LIST = '/alert/model/version/', VERSION_LIST = '/alert/model/version/',
VERSION_NEW = '/alert/model/version/new/', VERSION_NEW = '/alert/model/version/new/',
@ -42,8 +44,12 @@ export const getOptimisticApi = (params: any) => defHttp.get<any>({ url: Api.OPT
export const trainModelApi = (params: any) => defHttp.post<any>({ url: Api.TRAIN_MODEL, data: params }) export const trainModelApi = (params: any) => defHttp.post<any>({ url: Api.TRAIN_MODEL, data: params })
export const trainModelAnnApi = (params: any) => defHttp.post<any>({ url: Api.TRAIN_MODEL_ANN, data: params })
export const testModelApi = (params: any) => defHttp.post<any>({ url: Api.TEST_MODEL, data: params }) export const testModelApi = (params: any) => defHttp.post<any>({ url: Api.TEST_MODEL, data: params })
export const testModelAnnApi = (params: any) => defHttp.post<any>({ url: Api.TEST_MODEL_ANN, data: params })
export function bottomModelApi(id: any, reportId?: number | string) { export function bottomModelApi(id: any, reportId?: number | string) {
return defHttp.post<any>({ return defHttp.post<any>({
url: Api.BOTTOM_MODEL + id, url: Api.BOTTOM_MODEL + id,

14
src/views/model/AssessReport.vue

@ -258,10 +258,20 @@ export default defineComponent({
}, 0) }, 0)
} }
const normalizeTypeFlag = (val: any) => {
if (val === true || val === '是' || val === 1 || val === '1' || val === '输出' || val === 'output')
return true
if (val === false || val === '否' || val === 0 || val === '0' || val === '输入' || val === 'input')
return false
return !!val
}
const normalizePointRow = (item: any, index: number): AssessPointRow => { const normalizePointRow = (item: any, index: number): AssessPointRow => {
const tMax = item.TMax ?? item.tMax const tMax = item.TMax ?? item.tMax
const tMin = item.TMin ?? item.tMin const tMin = item.TMin ?? item.tMin
const locked = item.lock === true const locked = item.lock === true
const typeFlag = normalizeTypeFlag(item.type)
const isAnn = algorithm.value.toUpperCase() === 'ANN'
return { return {
...item, ...item,
description: item.description ?? item.Description, description: item.description ?? item.Description,
@ -275,8 +285,8 @@ export default defineComponent({
Upper: item.Upper ?? item.upper, Upper: item.Upper ?? item.upper,
Unit: item.Unit ?? item.unit, Unit: item.Unit ?? item.unit,
lock: locked, lock: locked,
// // ANN type true
alarm: !locked, alarm: !locked && (!isAnn || typeFlag === true),
} }
} }

22
src/views/model/list/step/Step2.vue

@ -48,10 +48,24 @@ export default defineComponent({
try { try {
const values = await validate() const values = await validate()
const modelInfo = toRaw(props.beforeData) const modelInfo = { ...(toRaw(props.beforeData) || {}) }
modelInfo.algorithm = values.algorithm const { algorithm, sampling, rate, iteration, hiddenLayer, iter } = values
modelInfo.sampling = values.sampling
modelInfo.rate = values.rate modelInfo.algorithm = algorithm
modelInfo.sampling = sampling
if (algorithm === 'PCA') {
modelInfo.rate = rate
delete modelInfo.iteration
delete modelInfo.layer
delete modelInfo.iter
}
else if (algorithm === 'ANN') {
modelInfo.iteration = iteration
modelInfo.layer = hiddenLayer
modelInfo.iter = iter
delete modelInfo.rate
}
emit('next', modelInfo) emit('next', modelInfo)
} }
catch (error) { catch (error) {

12
src/views/model/list/step/Step3.vue

@ -78,6 +78,7 @@ export default defineComponent({
unit: point.unit, unit: point.unit,
Lower: point.Lower, Lower: point.Lower,
Upper: point.Upper, Upper: point.Upper,
type: props.beforeData?.algorithm === 'ANN',
dead: true, dead: true,
limit: false, limit: false,
} }
@ -131,9 +132,18 @@ export default defineComponent({
<a-descriptions-item label="训练采样间隔"> <a-descriptions-item label="训练采样间隔">
{{ beforeData.sampling }} {{ beforeData.sampling }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="最小主元贡献率"> <a-descriptions-item v-if="beforeData.algorithm === 'PCA'" label="最小主元贡献率">
{{ beforeData.rate }} {{ beforeData.rate }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item v-if="beforeData.algorithm === 'ANN'" label="迭代次数">
{{ beforeData.iteration }}
</a-descriptions-item>
<a-descriptions-item v-if="beforeData.algorithm === 'ANN'" label="隐藏层结构">
{{ beforeData.layer }}
</a-descriptions-item>
<a-descriptions-item v-if="beforeData.algorithm === 'ANN'" label="最大迭代次数">
{{ beforeData.iter }}
</a-descriptions-item>
</a-descriptions> </a-descriptions>
<a-divider /> <a-divider />
<BasicForm @register="register"> <BasicForm @register="register">

10
src/views/model/list/step/data.tsx

@ -79,6 +79,16 @@ export const step2Schemas: FormSchema[] = [
colProps: { span: 24 }, colProps: { span: 24 },
labelWidth: 120, // 增大label宽度 labelWidth: 120, // 增大label宽度
}, },
{
field: 'iter',
component: 'InputNumber',
label: '最大迭代次数',
defaultValue: 300,
required: ({ values }) => values.algorithm === 'ANN',
ifShow: ({ values }) => values.algorithm === 'ANN',
colProps: { span: 24 },
labelWidth: 120,
},
] ]
export const step3Schemas: FormSchema[] = [ export const step3Schemas: FormSchema[] = [

6
src/views/model/train/data.tsx

@ -155,6 +155,12 @@ export const pointTableSchema: BasicColumn[] = [
width: 120, width: 120,
dataIndex: 'limit', dataIndex: 'limit',
}, },
{
title: '测点类型',
width: 120,
dataIndex: 'type',
slots: { customRender: 'pointType' },
},
{ {
title: '编辑', title: '编辑',
width: 100, width: 100,

208
src/views/model/train/index.vue

@ -17,15 +17,16 @@ import {
Input, Input,
InputNumber, InputNumber,
Modal, Modal,
Radio,
RangePicker, RangePicker,
Row, Row,
Select, Select,
Space, Space,
Spin, Spin,
Steps, Steps,
Switch,
Table, Table,
Tabs, Tabs,
Radio,
} from 'ant-design-vue' } from 'ant-design-vue'
import VueECharts from 'vue-echarts' import VueECharts from 'vue-echarts'
import PointTransfer from '../components/PointTransfer.vue' import PointTransfer from '../components/PointTransfer.vue'
@ -37,7 +38,9 @@ import {
bottomModelApi, bottomModelApi,
createDraftVersionApi, createDraftVersionApi,
modelInfoApi, modelInfoApi,
testModelAnnApi,
testModelApi, testModelApi,
trainModelAnnApi,
trainModelApi, trainModelApi,
updateModelInfo, updateModelInfo,
versionListApi, versionListApi,
@ -79,6 +82,7 @@ export default defineComponent({
ASpace: Space, ASpace: Space,
ARadio: Radio, ARadio: Radio,
ARadioGroup: Radio.Group, ARadioGroup: Radio.Group,
ASwitch: Switch,
Icon, Icon,
PointTransfer, PointTransfer,
}, },
@ -134,12 +138,20 @@ export default defineComponent({
const pointData = computed(() => { const pointData = computed(() => {
const list = model.value?.pointInfo || [] const list = model.value?.pointInfo || []
const normalizeBool = (val: any) => {
if (val === true || val === '是' || val === 1 || val === '1' || val === '输出' || val === 'output')
return true
if (val === false || val === '否' || val === 0 || val === '0' || val === '输入' || val === 'input')
return false
return !!val
}
return list.map((p: any) => ({ return list.map((p: any) => ({
description: p.description ?? p.Description, description: p.description ?? p.Description,
PointId: p.PointId ?? p.pointId, PointId: p.PointId ?? p.pointId,
unit: p.unit ?? p.Unit, unit: p.unit ?? p.Unit,
Upper: p.Upper ?? p.upper, Upper: p.Upper ?? p.upper,
Lower: p.Lower ?? p.lower, Lower: p.Lower ?? p.lower,
type: normalizeBool(p.type),
dead: p.dead === true ? '是' : '否', dead: p.dead === true ? '是' : '否',
limit: p.limit === true ? '是' : '否', limit: p.limit === true ? '是' : '否',
upperBound: p.upperBound ?? p.upperbound ?? p.upperBound, upperBound: p.upperBound ?? p.upperbound ?? p.upperBound,
@ -174,6 +186,26 @@ export default defineComponent({
return sum + (Number.isFinite(modeVal) ? modeVal : 0) return sum + (Number.isFinite(modeVal) ? modeVal : 0)
}, 500) }, 500)
}) })
function handlePointTypeChange(pointId: any, value: boolean) {
if (!isANNAlgorithm.value)
return
if (!showTrainActions.value) {
createMessage.warning('非草稿版本不可修改测点类型')
return
}
const infoList = model.value?.pointInfo
if (!Array.isArray(infoList))
return
const idx = infoList.findIndex(item => (item?.PointId ?? item?.pointId) === pointId)
if (idx === -1)
return
infoList[idx] = {
...infoList[idx],
type: value,
}
model.value.pointInfo = [...infoList]
updateModelInfoDebounced()
}
const activeKey = ref('1') const activeKey = ref('1')
type RangeValue = [Dayjs, Dayjs] type RangeValue = [Dayjs, Dayjs]
@ -230,18 +262,43 @@ export default defineComponent({
createMessage.error('未获取到模型信息,无法获取测试数据') createMessage.error('未获取到模型信息,无法获取测试数据')
return return
} }
const algorithm = (model.value?.algorithm || 'PCA').toUpperCase()
const timeStr = timeRange
.map(t => dayjs(t).format('YYYY-MM-DD HH:mm:ss'))
.join(',')
const pointStr = model.value.pointInfo.map(t => t.PointId).join(',')
const intervalMs = model.value.sampling * 1000
let result
if (algorithm === 'ANN') {
const normalizeTypeFlag = (val: any) => {
if (val === true || val === '是' || val === 1 || val === '1' || val === '输出' || val === 'output')
return true
if (val === false || val === '否' || val === 0 || val === '0' || val === '输入' || val === 'input')
return false
return !!val
}
const typeStr = model.value.pointInfo.map(t => (normalizeTypeFlag(t?.type) ? '1' : '0')).join(',')
const params = {
time: timeStr,
points: pointStr,
interval: intervalMs,
model: JSON.stringify(model.value?.para ?? ''),
type: typeStr,
}
result = await testModelAnnApi(params)
}
else {
const params = { const params = {
Model_id: id, Model_id: id,
version: model.value?.version ? model.value?.version : 'v-test', version: model.value?.version ? model.value?.version : 'v-test',
Test_Data: { Test_Data: {
time: timeRange time: timeStr,
.map(t => dayjs(t).format('YYYY-MM-DD HH:mm:ss')) points: pointStr,
.join(','), interval: intervalMs,
points: model.value.pointInfo.map(t => t.PointId).join(','),
interval: model.value.sampling * 1000,
}, },
} }
const result = await testModelApi(params) result = await testModelApi(params)
}
const sampleData = result?.sampleData ?? result?.SampleData ?? [] const sampleData = result?.sampleData ?? result?.SampleData ?? []
const reconData = result?.reconData ?? result?.ReconData ?? [] const reconData = result?.reconData ?? result?.ReconData ?? []
if (!Array.isArray(sampleData) || !Array.isArray(reconData) || sampleData.length === 0 || reconData.length === 0) { if (!Array.isArray(sampleData) || !Array.isArray(reconData) || sampleData.length === 0 || reconData.length === 0) {
@ -561,35 +618,59 @@ export default defineComponent({
console.error('模型参数点信息为空,无法训练') console.error('模型参数点信息为空,无法训练')
return return
} }
const params = { const algorithm = (modelInfo.algorithm || 'PCA').toUpperCase()
conditon: modelInfo.alarmmodelset?.alarmcondition || '1=1', const condition = modelInfo.alarmmodelset?.alarmcondition || '1=1'
Hyper_para: { const trainData = {
percent: modelInfo.rate,
},
Train_Data: {
points: pointInfo.map(item => item.PointId).join(','), points: pointInfo.map(item => item.PointId).join(','),
dead: pointInfo.map(item => (item.dead ? '1' : '0')).join(','), dead: pointInfo.map(item => (item.dead ? '1' : '0')).join(','),
limit: pointInfo.map(item => (item.limit ? '1' : '0')).join(','), limit: pointInfo.map(item => (item.limit ? '1' : '0')).join(','),
uplow: pointInfo uplow: pointInfo
.map( .map(
item => item =>
`${item.Upper ? item.Upper : null},${ `${item.Upper ?? ''},${item.Lower ?? ''}`,
item.Lower ? item.Lower : null
}`,
) )
.join(';'), .join(';'),
interval: modelInfo.sampling * 1000, interval: modelInfo.sampling * 1000,
time: modelInfo.trainTime time: modelInfo.trainTime.map(item => `${item.st},${item.et}`).join(';'),
.map(item => `${item.st},${item.et}`)
.join(';'),
},
type: 'PCA',
smote_config: [],
smote: true,
} }
spinning.value = true spinning.value = true
try { try {
const response = await trainModelApi(params) let response
if (algorithm === 'ANN') {
const normalizeTypeFlag = (val: any) => {
if (val === true || val === '是' || val === 1 || val === '1' || val === '输出' || val === 'output')
return true
if (val === false || val === '否' || val === 0 || val === '0' || val === '输入' || val === 'input')
return false
return !!val
}
const typeStr = pointInfo.map(item => (normalizeTypeFlag(item?.type) ? '1' : '0')).join(',')
const params = {
type: typeStr,
iter: modelInfo.iter,
hide: modelInfo.layer,
conditon: condition,
condition,
Train_Data: trainData,
}
response = await trainModelAnnApi(params)
response = JSON.parse(response)
response.filename = JSON.parse(response.filename)
}
else {
const hyperPara: Record<string, any> = {}
if (algorithm === 'PCA')
hyperPara.percent = modelInfo.rate
const params = {
conditon: condition,
Hyper_para: hyperPara,
Train_Data: trainData,
type: algorithm,
smote_config: [],
smote: true,
}
response = await trainModelApi(params)
}
model.value.para = response model.value.para = response
const modelInfoDetail = response.Model_info || response.modelInfo const modelInfoDetail = response.Model_info || response.modelInfo
@ -770,8 +851,13 @@ export default defineComponent({
const editModelForm = ref({ const editModelForm = ref({
sampling: 0, sampling: 0,
rate: 0, rate: 0,
iter: 0,
layer: '',
selectedKeys: [], selectedKeys: [],
}) })
const algorithmValue = computed(() => (model.value?.algorithm || 'PCA').toUpperCase())
const isPCAAlgorithm = computed(() => algorithmValue.value === 'PCA')
const isANNAlgorithm = computed(() => algorithmValue.value === 'ANN')
function openEditModel() { function openEditModel() {
if (!canEditModel.value) { if (!canEditModel.value) {
@ -780,6 +866,8 @@ export default defineComponent({
} }
editModelForm.value.sampling = model.value?.sampling || 0 editModelForm.value.sampling = model.value?.sampling || 0
editModelForm.value.rate = model.value?.rate || 0 editModelForm.value.rate = model.value?.rate || 0
editModelForm.value.iter = model.value?.iter || 0
editModelForm.value.layer = model.value?.layer || ''
editModelForm.value.selectedKeys = (model.value?.pointInfo || []).map(item => buildPointKeyFromInfo({ editModelForm.value.selectedKeys = (model.value?.pointInfo || []).map(item => buildPointKeyFromInfo({
description: item.description ?? item.Description, description: item.description ?? item.Description,
PointId: item.PointId ?? item.pointId, PointId: item.PointId ?? item.pointId,
@ -795,7 +883,22 @@ export default defineComponent({
return return
} }
model.value.sampling = editModelForm.value.sampling model.value.sampling = editModelForm.value.sampling
if (isPCAAlgorithm.value) {
model.value.rate = editModelForm.value.rate model.value.rate = editModelForm.value.rate
delete model.value.layer
delete model.value.iter
}
else if (isANNAlgorithm.value) {
model.value.layer = editModelForm.value.layer
model.value.iter = editModelForm.value.iter
if (!Array.isArray(model.value.pointInfo))
model.value.pointInfo = []
model.value.pointInfo = model.value.pointInfo.map((item: any) => ({
...item,
type: item?.type ?? true,
}))
delete model.value.rate
}
model.value.pointInfo = editModelForm.value.selectedKeys.map((key) => { model.value.pointInfo = editModelForm.value.selectedKeys.map((key) => {
const { description, PointId, unit, Lower, Upper } = parsePointKey(key) const { description, PointId, unit, Lower, Upper } = parsePointKey(key)
return { return {
@ -804,6 +907,7 @@ export default defineComponent({
unit, unit,
Lower, Lower,
Upper, Upper,
type: isANNAlgorithm.value ? true : undefined,
dead: true, dead: true,
limit: false, limit: false,
} }
@ -899,9 +1003,9 @@ export default defineComponent({
else { else {
queryParts.push('sampleType=train') queryParts.push('sampleType=train')
} }
if (assessSampleCount.value !== null && assessSampleCount.value !== undefined && assessSampleCount.value !== '') { if (assessSampleCount.value !== null && assessSampleCount.value !== undefined && assessSampleCount.value !== '')
queryParts.push(`sampleCount=${assessSampleCount.value}`) queryParts.push(`sampleCount=${assessSampleCount.value}`)
}
assessConfigVisible.value = false assessConfigVisible.value = false
go(`/model/assess-report/${id}?${queryParts.join('&')}`) go(`/model/assess-report/${id}?${queryParts.join('&')}`)
} }
@ -1135,6 +1239,9 @@ export default defineComponent({
createDraftVersion, createDraftVersion,
goAssessReport, goAssessReport,
showTrainActions, showTrainActions,
isPCAAlgorithm,
isANNAlgorithm,
handlePointTypeChange,
canEditModel, canEditModel,
effectiveSampleCount, effectiveSampleCount,
assessConfigVisible, assessConfigVisible,
@ -1219,12 +1326,18 @@ export default defineComponent({
<a-descriptions-item label="参数个数"> <a-descriptions-item label="参数个数">
{{ model?.pointInfo.length || "暂无" }} {{ model?.pointInfo.length || "暂无" }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="最小主元贡献率"> <a-descriptions-item v-if="isPCAAlgorithm" label="最小主元贡献率">
{{ model?.rate }} {{ model?.rate }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item label="最小主元个数"> <a-descriptions-item v-if="isPCAAlgorithm" label="最小主元个数">
{{ model?.principal }} {{ model?.principal }}
</a-descriptions-item> </a-descriptions-item>
<a-descriptions-item v-if="isANNAlgorithm" label="隐藏层结构">
{{ model?.layer || '暂无' }}
</a-descriptions-item>
<a-descriptions-item v-if="isANNAlgorithm" label="最大迭代次数">
{{ model?.iter }}
</a-descriptions-item>
<a-descriptions-item label="模型性能"> <a-descriptions-item label="模型性能">
{{ model?.precision }} {{ model?.precision }}
</a-descriptions-item> </a-descriptions-item>
@ -1272,6 +1385,18 @@ export default defineComponent({
</a-tab-pane> </a-tab-pane>
<a-tab-pane key="2" tab="测点参数"> <a-tab-pane key="2" tab="测点参数">
<BasicTable class="compact-table" @register="pointTable"> <BasicTable class="compact-table" @register="pointTable">
<template #pointType="{ record }">
<template v-if="isANNAlgorithm">
<a-switch
:checked="record.type"
:disabled="!showTrainActions"
checked-children="输出"
un-checked-children="输入"
@change="(val) => handlePointTypeChange(record.PointId, val as boolean)"
/>
</template>
<span v-else>--</span>
</template>
<template #action="{ record, index }"> <template #action="{ record, index }">
<a-button <a-button
v-if="showTrainActions" v-if="showTrainActions"
@ -1522,7 +1647,7 @@ export default defineComponent({
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col :span="12"> <a-col v-if="isPCAAlgorithm" :span="12">
<a-form-item label="最小主元贡献率" required> <a-form-item label="最小主元贡献率" required>
<a-input-number <a-input-number
v-model:value="editModelForm.rate" v-model:value="editModelForm.rate"
@ -1531,6 +1656,25 @@ export default defineComponent({
/> />
</a-form-item> </a-form-item>
</a-col> </a-col>
<a-col v-if="isANNAlgorithm" :span="12">
<a-form-item label="隐藏层结构" required>
<a-input
v-model:value="editModelForm.layer"
placeholder="请输入隐藏层结构"
/>
</a-form-item>
</a-col>
</a-row>
<a-row v-if="isANNAlgorithm" :gutter="16">
<a-col :span="12">
<a-form-item label="最大迭代次数" required>
<a-input-number
v-model:value="editModelForm.iter"
placeholder="请输入最大迭代次数"
style="width: 100%"
/>
</a-form-item>
</a-col>
</a-row> </a-row>
<div style="display: flex; justify-content: center; margin-top: 24px;margin-bottom: 10px;"> <div style="display: flex; justify-content: center; margin-top: 24px;margin-bottom: 10px;">
<PointTransfer <PointTransfer
@ -1563,8 +1707,8 @@ export default defineComponent({
.assess-option { .assess-option {
display: flex; display: flex;
align-items: center;
gap: 8px; gap: 8px;
align-items: center;
} }
.assess-label { .assess-label {
@ -1573,8 +1717,8 @@ export default defineComponent({
} }
.assess-hint { .assess-hint {
color: #9098a4;
font-size: 12px; font-size: 12px;
color: #9098a4;
} }
.compact-card { .compact-card {
@ -1639,7 +1783,7 @@ export default defineComponent({
} }
.tab-card :deep(.ant-card-body) { .tab-card :deep(.ant-card-body) {
padding: 4px 6px 4px; padding: 4px 6px;
} }
.train-toolbar { .train-toolbar {

Loading…
Cancel
Save