From b98b9431aec778fa2bea6828a00bfb59013e7c80 Mon Sep 17 00:00:00 2001 From: ArvinHuang <1095369685@qq.com> Date: Fri, 15 Dec 2023 18:51:23 +0800 Subject: [PATCH] update: 1. heartbeat for running list. 2. running status. 3. pearson display. 4. refresh operation for detail. 5. homo_lr component supported. Signed-off-by: ArvinHuang <1095369685@qq.com> --- .../src/components/Pearson/Pearson.vue | 4 + .../fate-board/src/store/modules/comp.ts | 18 ++- .../fate-board/src/store/modules/job.ts | 21 ++-- .../fate-board/src/transform/index.ts | 2 +- .../fate-board/src/transform/metric/loss.ts | 38 ++++-- .../transform/model/feature_correlation.ts | 4 +- .../fate-board/src/transform/model/homo_lr.ts | 97 ++++++++++++++- .../fate-board/src/transform/style.scss | 18 +-- .../fate-board/src/transform/tools/toTable.ts | 4 +- .../src/views/dashboard/Gauge/Gauge.vue | 6 +- .../fate-board/src/views/detail/Detail.vue | 4 +- .../views/detail/Information/DataOutput.vue | 15 +++ .../views/detail/Information/LogOutput.vue | 19 +++ .../views/detail/Information/ModelOutput.vue | 68 +++++++++++ .../src/views/detail/Information/outputs.vue | 110 +++++++++--------- .../src/views/history/filter/Filter.vue | 4 +- .../fate-board/src/views/running/Running.vue | 2 +- .../lib/UIParse/AST/dataAsync.ts | 7 ++ .../fate-ui-component/lib/UIParse/AST/node.ts | 6 + .../fate-ui-component/lib/UIParse/nodeVue.ts | 6 + .../lib/UIParse/utils/Tree.ts | 11 ++ .../VirtualScroll/VirtualScroll.vue | 18 --- .../VirtualScroll/VirtualScrollItem.vue | 6 +- .../fate-visualization/html/DAGGraphicApp.vue | 13 ++- .../packages/fate-visualization/html/main.ts | 4 +- .../fate-visualization/lib/DAGGraphic/DAG.vue | 4 +- .../lib/DAGGraphic/DAGContainer.ts | 2 +- .../lib/DAGGraphic/component/configuration.ts | 1 + .../lib/DAGGraphic/runningStage.ts | 5 +- 29 files changed, 383 insertions(+), 134 deletions(-) create mode 100644 resources-front-end/packages/fate-board/src/views/detail/Information/ModelOutput.vue diff --git a/resources-front-end/packages/fate-board/src/components/Pearson/Pearson.vue b/resources-front-end/packages/fate-board/src/components/Pearson/Pearson.vue index bd6a4510..228226ba 100644 --- a/resources-front-end/packages/fate-board/src/components/Pearson/Pearson.vue +++ b/resources-front-end/packages/fate-board/src/components/Pearson/Pearson.vue @@ -162,6 +162,10 @@ watch( width: calc(100% - 450px); min-height: 600px; @include flex-col(); + padding: 0px $pale; + background: var(--el-bg-color); + border: 1px solid var(--el-color-info-light-9); + border-radius: 2px; } .f-p-map-header { diff --git a/resources-front-end/packages/fate-board/src/store/modules/comp.ts b/resources-front-end/packages/fate-board/src/store/modules/comp.ts index c39d817f..00a79eab 100644 --- a/resources-front-end/packages/fate-board/src/store/modules/comp.ts +++ b/resources-front-end/packages/fate-board/src/store/modules/comp.ts @@ -82,8 +82,10 @@ export default { }, actions: { - async chooseComp({ state, commit, dispatch }: any, comp: any) { - commit('SET_INFORMATION', comp); + async chooseComp({ state, commit, dispatch }: any, comp?: any) { + if (comp) { + commit('SET_INFORMATION', comp); + } await dispatch('parameterRequest'); await dispatch('modelRequest'); await dispatch('metricRequest'); @@ -131,7 +133,7 @@ export default { async modelRequest({ state, commit, dispatch }: any) { try { - if (!state.hasLoaded[state.information.name] || !state.hasLoaded[state.information.name].configuration) { + if (!state.hasLoaded[state.information.name] || !state.hasLoaded[state.information.name].instance) { const job_id = await dispatch('GET_JOBID'); const party_id = await dispatch('GET_PARTYID'); const role = await dispatch('GET_JOB_ROLE'); @@ -150,7 +152,7 @@ export default { async metricRequest({ state, commit, dispatch }: any) { try { - if (!state.hasLoaded[state.information.name] || !state.hasLoaded[state.information.name].configuration) { + if (!state.hasLoaded[state.information.name] || !state.hasLoaded[state.information.name].instance) { const job_id = await dispatch('GET_JOBID'); const party_id = await dispatch('GET_PARTYID'); const role = await dispatch('GET_JOB_ROLE'); @@ -167,6 +169,14 @@ export default { } }, + modelRefresh ({ state }: any) { + const component = state.information.name + if (component && state.hasLoaded[component]) { + state.hasLoaded[component].instance?.release() + delete state.hasLoaded[component] + } + }, + setLoader({ state, commit }: any, instance: any) { const name = state.information.name; if (name) { diff --git a/resources-front-end/packages/fate-board/src/store/modules/job.ts b/resources-front-end/packages/fate-board/src/store/modules/job.ts index 58e46abd..2816b96b 100644 --- a/resources-front-end/packages/fate-board/src/store/modules/job.ts +++ b/resources-front-end/packages/fate-board/src/store/modules/job.ts @@ -2,7 +2,7 @@ import API from '@/api'; import dagExplaination from '@/utils/dagExplaination'; import { ElMessage } from 'element-plus'; import { WSConnect } from 'fate-tools'; -import { merge } from 'lodash'; +import { merge, throttle } from 'lodash'; export default { state: { @@ -46,18 +46,17 @@ export default { actions: { async JOB_INFORMATION({ state, commit }: any) { if (state._ws_) return true; + + // 首次加载标识 + let firstTimeHandle = true const handler = (data: any) => { dagExplaination(data.dependency_data, (res: any) => { - if (Object.keys(state.dag).length > 0) { - - } else { - commit('SET_DAG', res) - } + commit('SET_DAG', res) }) commit('SET_DATASET', data.summary_date.dataset); commit('SET_DETAILS', data.summary_date.job); if (data.status && !data.status.match(/running|waiting/)) { - if (!state._rerun_) { + if (!state._rerun_ && state._ws_) { state._ws_.close() commit('SET_WS', undefined) } @@ -65,6 +64,7 @@ export default { if (state._rerun_) state._rerun_ = false } }; + const throttleHandler = throttle(handler, 10000) if (!state.role || !state.jobId || !state.partyId) { return false; } else { @@ -75,7 +75,12 @@ export default { let data; try { data = JSON.parse(event.data); - handler(data); + if (firstTimeHandle) { + handler(data); + firstTimeHandle = false + } else { + throttleHandler(data) + } } catch (error) { state._ws_.close(); data = null; diff --git a/resources-front-end/packages/fate-board/src/transform/index.ts b/resources-front-end/packages/fate-board/src/transform/index.ts index e541fbad..a73dd52b 100644 --- a/resources-front-end/packages/fate-board/src/transform/index.ts +++ b/resources-front-end/packages/fate-board/src/transform/index.ts @@ -16,7 +16,7 @@ export default function explain ( id: 'ComponentDetailContainer', tag: 'article', prop: { - class: 'f-detail-component', + class: 'f-detail-component f-d-seperator', }, children: (() => { const children: any = [] diff --git a/resources-front-end/packages/fate-board/src/transform/metric/loss.ts b/resources-front-end/packages/fate-board/src/transform/metric/loss.ts index a3f999e8..40de1b0f 100644 --- a/resources-front-end/packages/fate-board/src/transform/metric/loss.ts +++ b/resources-front-end/packages/fate-board/src/transform/metric/loss.ts @@ -8,22 +8,37 @@ export default function Loss ( const configuration = { xAxis: { type: 'category', + name: 'epoch' }, yAxis: { type: 'value', name: 'loss' }, - series:[{ - type: 'line', - name: 'loss', - data: (() => { - const list = [] - for (const each of data) { - list.push(fixed(each.metric)) + series: (() => { + let cursor = 0 + const result = [] + const pointers = [] + for (const each of data) { + if (each.step === 0 && pointers.length > 0) { + result.push({ + type: 'line', + name: `loss_${cursor ++}`, + data: [...pointers] + }) + pointers.length = 0 } - return list - })() - }] + pointers.push([each.step, fixed(each.metric)]) + } + if (pointers.length > 0) { + result.push({ + type: 'line', + name: `loss_${cursor}`, + data: [...pointers] + }) + pointers.length = 0 + } + return result + })() } return { @@ -31,7 +46,8 @@ export default function Loss ( tag: FLine, prop: { title: 'Loss', - data: configuration + data: configuration, + legend: 1 } } } \ No newline at end of file diff --git a/resources-front-end/packages/fate-board/src/transform/model/feature_correlation.ts b/resources-front-end/packages/fate-board/src/transform/model/feature_correlation.ts index 6e132712..7e0f917d 100644 --- a/resources-front-end/packages/fate-board/src/transform/model/feature_correlation.ts +++ b/resources-front-end/packages/fate-board/src/transform/model/feature_correlation.ts @@ -24,7 +24,7 @@ export default function feature_correlation ( }] let tdata = [] - const hasAnony = Object.keys(column_anonymous_map).length > 0 + const hasAnony = Object.keys(column_anonymous_map || {}).length > 0 if (hasAnony) { theader.push({ label: 'anonym', @@ -60,7 +60,7 @@ export default function feature_correlation ( } } - let remote_key = Object.keys(remote_corr) + let remote_key = Object.keys(remote_corr || {}) if (remote_key && remote_key.length > 0) { remote_key = sort(remote_key) } diff --git a/resources-front-end/packages/fate-board/src/transform/model/homo_lr.ts b/resources-front-end/packages/fate-board/src/transform/model/homo_lr.ts index 494b8932..3375e655 100644 --- a/resources-front-end/packages/fate-board/src/transform/model/homo_lr.ts +++ b/resources-front-end/packages/fate-board/src/transform/model/homo_lr.ts @@ -1,4 +1,8 @@ +import fixed from "../tools/fixed"; import getModelData from "../tools/getModelData"; +import toSelect from "../tools/toSelect"; +import toTable from "../tools/toTable"; +import toText from "../tools/toText"; export default function HomoLr ( modelData: object, @@ -12,9 +16,98 @@ export default function HomoLr ( const { param, meta } = modelInstance.train_output_model; const { ovr, max_iter } = meta - const { state_dict, label_num, feature_num } = param.model + const { state_dict, label_num, feature_num, model_name } = param.model const isGuest = role.match(/guest/i) const isHost = role.match(/host/i) - if (ovr) {} + const options = [] + const theader = [{ + label: 'variable', + prop: 'variable' + }, { + prop: 'weight', + label: 'weight', + sortable: true, + }] + const tBias = {} + const tData = {} + + for (const key in state_dict) { + const [ _model, modelLabel, parameter ] = key.split('.') + const optionIndex = options.findIndex((item: any) => item.prop === modelLabel) + if (optionIndex < 0) { + options.push({ + label: `model_${modelLabel}`, + prop: modelLabel + }) + } + if (parameter.match(/bias/i)) { + tBias[modelLabel] = fixed(state_dict[key][0]) + } else if (parameter.match(/weight/i)) { + const tDataForModel = [] + for (let i = 0 ; i < state_dict[key][0].length; i++) { + tDataForModel.push({ + variable: model_name?.[i] || `x${i}`, + weight: fixed(state_dict[key][0][i]) + }) + } + tData[modelLabel] = tDataForModel + } + } + + const homo_lr = { + id: 'LRModelContainer', + tag: 'section', + prop: { class: 'f-d-container f-d-margin' }, + children: [], + }; + const hasSelection = options && options.length > 1; + + if (hasSelection) { + homo_lr.children.push( + toSelect('LROVRSelection', options, { + placeholder: '', + label: 'one_vs_rest model', + }), + toText( + { + request: (value: any) => { + const index = options.findIndex((item: any) => item.prop === value) + return options[index].label + }, + parameter: ['LROVRSelection.modelValue'], + }, + 'Model Label' + ) + ); + } + + homo_lr.children.push( + toText( + hasSelection ? { + request: (value: any) => { + return tBias[value]; + }, + parameter: ['LROVRSelection.modelValue'], + } : tBias[options[0].prop], + 'Bias' + ) + ) + + homo_lr.children.push( + toTable( + theader, + hasSelection ? { + request: (value: any) => { + return tData[value] + }, + parameter: ['LROVRSelection.modelValue'], + } : tData[options[0].prop], + { + index: true + } + ) + ) + + return homo_lr } \ No newline at end of file diff --git a/resources-front-end/packages/fate-board/src/transform/style.scss b/resources-front-end/packages/fate-board/src/transform/style.scss index e370fb0d..5691a2a8 100644 --- a/resources-front-end/packages/fate-board/src/transform/style.scss +++ b/resources-front-end/packages/fate-board/src/transform/style.scss @@ -1,18 +1,22 @@ @import '@/style/index.scss'; +.f-d-seperator { + & > * { + margin-bottom: $pale * 3; + border-bottom: 1px solid var(--el-color-info-light-7); + + &:last-child { + border: 0px; + } + } +} + .f-detail-component { width: 100%; @include flex-col(); align-items: flex-start; justify-content: flex-start; - .f-d-seperator { - & > * { - margin-bottom: $pale * 3; - border-bottom: 1px solid var(--el-color-info-light-7); - } - } - .f-d-margin { & > * { margin-bottom: $pale; diff --git a/resources-front-end/packages/fate-board/src/transform/tools/toTable.ts b/resources-front-end/packages/fate-board/src/transform/tools/toTable.ts index 1d8975ae..62747706 100644 --- a/resources-front-end/packages/fate-board/src/transform/tools/toTable.ts +++ b/resources-front-end/packages/fate-board/src/transform/tools/toTable.ts @@ -7,9 +7,11 @@ export default function toTable(header: any, data: any, ext?: any) { tag: FTable, prop: Object.assign({ class: 'f-d-table', - maxHeight: '400px', + maxHeight: '450px', header, data, + size: 10, + total: true }, ext || {}), }; } diff --git a/resources-front-end/packages/fate-board/src/views/dashboard/Gauge/Gauge.vue b/resources-front-end/packages/fate-board/src/views/dashboard/Gauge/Gauge.vue index d9d8028c..989f1e76 100644 --- a/resources-front-end/packages/fate-board/src/views/dashboard/Gauge/Gauge.vue +++ b/resources-front-end/packages/fate-board/src/views/dashboard/Gauge/Gauge.vue @@ -61,19 +61,19 @@ const toDetail = () => { const elapsed = () => { let elapsed: any = store.state.job.details.fElapsed if (!elapsed) { - elapsed = Math.floor((store.state.job.details.fStartTime ? (new Date().getTime() - store.state.job.details.fStartTime) : 0) / 1000) + elapsed = Math.floor((store.state.job.details.fStartTime ? (new Date().getTime() - store.state.job.details.fStartTime) : 0)) } duration.value = elapsed } const elapsing = () => { setTimeout(() => { if (status.value.match(/running/i)) { - duration.value += 1 + duration.value += 1000 elapsing() } else { elapsed() } - }, 950) + }, 1000) } watch( diff --git a/resources-front-end/packages/fate-board/src/views/detail/Detail.vue b/resources-front-end/packages/fate-board/src/views/detail/Detail.vue index d603f0e7..3bfbc472 100644 --- a/resources-front-end/packages/fate-board/src/views/detail/Detail.vue +++ b/resources-front-end/packages/fate-board/src/views/detail/Detail.vue @@ -22,7 +22,7 @@ - + diff --git a/resources-front-end/packages/fate-board/src/views/detail/Information/outputs.vue b/resources-front-end/packages/fate-board/src/views/detail/Information/outputs.vue index b5c6820b..ac58650c 100644 --- a/resources-front-end/packages/fate-board/src/views/detail/Information/outputs.vue +++ b/resources-front-end/packages/fate-board/src/views/detail/Information/outputs.vue @@ -9,29 +9,34 @@ :modal="false" class="f-output-dialog" > - - - - - - - - - - - +
+
+ Refresh +
+ + + + + + + + + + + +
diff --git a/resources-front-end/packages/fate-visualization/html/main.ts b/resources-front-end/packages/fate-visualization/html/main.ts index 9a2a3845..f06f09e5 100644 --- a/resources-front-end/packages/fate-visualization/html/main.ts +++ b/resources-front-end/packages/fate-visualization/html/main.ts @@ -3,8 +3,8 @@ import 'element-plus/dist/index.css'; import { createApp } from 'vue'; import * as Visual from '../lib/main'; // import App from './LineOrBarApp.vue'; -// import App from './DAGGraphicApp.vue'; -import App from './HeatMap.vue'; +import App from './DAGGraphicApp.vue'; +// import App from './HeatMap.vue'; // import App from './Tree.vue'; // import App from './Gauge.vue'; diff --git a/resources-front-end/packages/fate-visualization/lib/DAGGraphic/DAG.vue b/resources-front-end/packages/fate-visualization/lib/DAGGraphic/DAG.vue index ff1bc14f..0304c048 100644 --- a/resources-front-end/packages/fate-visualization/lib/DAGGraphic/DAG.vue +++ b/resources-front-end/packages/fate-visualization/lib/DAGGraphic/DAG.vue @@ -80,8 +80,8 @@ watch( DAGInstance.release() DAGCreator() } else { - for (const comp of (newValue?.component_lists || [])) { - DAGInstance.setStatus(comp.component_name, comp.status, (new Date().getTime() - comp.time || 0)) + for (const comp of (newValue?.component_list || [])) { + DAGInstance.setStatus(comp.component_name, runningStatus(comp.status), (new Date().getTime() - comp.time || 0)) } } } diff --git a/resources-front-end/packages/fate-visualization/lib/DAGGraphic/DAGContainer.ts b/resources-front-end/packages/fate-visualization/lib/DAGGraphic/DAGContainer.ts index ac360e4b..a391e1c9 100644 --- a/resources-front-end/packages/fate-visualization/lib/DAGGraphic/DAGContainer.ts +++ b/resources-front-end/packages/fate-visualization/lib/DAGGraphic/DAGContainer.ts @@ -366,7 +366,7 @@ export default class DAG { setStatus(name: string, status: string, duration: number) { const comp = this.component.get(name); - if (comp) { + if (comp && comp.prop.status !== status) { const tiktok = comp.find('tiktok'); if (tiktok) { tiktok.setProp('duration', duration); diff --git a/resources-front-end/packages/fate-visualization/lib/DAGGraphic/component/configuration.ts b/resources-front-end/packages/fate-visualization/lib/DAGGraphic/component/configuration.ts index 85e02712..fbf3c0f5 100644 --- a/resources-front-end/packages/fate-visualization/lib/DAGGraphic/component/configuration.ts +++ b/resources-front-end/packages/fate-visualization/lib/DAGGraphic/component/configuration.ts @@ -25,6 +25,7 @@ export default { stagePredict: '#4159D1', stageTrain: '#73767a', + stageCross_validation: '#b88230', // rnutime status: unrun Unrun_Body: '#E8E8EF', diff --git a/resources-front-end/packages/fate-visualization/lib/DAGGraphic/runningStage.ts b/resources-front-end/packages/fate-visualization/lib/DAGGraphic/runningStage.ts index 58d36489..eef5ec9e 100644 --- a/resources-front-end/packages/fate-visualization/lib/DAGGraphic/runningStage.ts +++ b/resources-front-end/packages/fate-visualization/lib/DAGGraphic/runningStage.ts @@ -1,6 +1,7 @@ enum CompStage { PREDICE = 'PREDICT', TRAIN = 'TRAIN', + CROSS_VALIDATION = 'CROSS_VALIDATION' } export default function runningStage(stage: string = '') { @@ -8,7 +9,9 @@ export default function runningStage(stage: string = '') { return 'predict'; } else if (stage.match(new RegExp(`(${CompStage.TRAIN})`, 'i'))) { return 'train'; + } else if (stage.match(new RegExp(`(${CompStage.CROSS_VALIDATION})`, 'i'))) { + return 'cross_validation'; } else { - return 'default'; + return 'default' } }