You can not select more than 25 topics Topics must start with a chinese character,a letter or number, can include dashes ('-') and can be up to 35 characters long.

jobForm.vue 30 kB

4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
4 years ago
123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856
  1. /** Copyright 2020 Zhejiang Lab. All Rights Reserved.
  2. *
  3. * Licensed under the Apache License, Version 2.0 (the "License");
  4. * you may not use this file except in compliance with the License.
  5. * You may obtain a copy of the License at
  6. *
  7. * http://www.apache.org/licenses/LICENSE-2.0
  8. *
  9. * Unless required by applicable law or agreed to in writing, software
  10. * distributed under the License is distributed on an "AS IS" BASIS,
  11. * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. * See the License for the specific language governing permissions and
  13. * limitations under the License.
  14. * =============================================================
  15. */
  16. <!--使用场景: add: 任务创建(jobAdd), edit:任务版本修改(jobDetail),paramAdd:模板创建(jobDetail),paramEdit:模板修改(job)-->
  17. <template>
  18. <div>
  19. <el-form
  20. ref="form"
  21. :model="form"
  22. :rules="rules"
  23. label-width="120px"
  24. :style="`width: ${widthPercent}%; margin-top: 20px;`"
  25. >
  26. <el-form-item v-if="type==='add' || type === 'paramsAdd' || type === 'algoAdd'" label="任务名称" prop="trainName">
  27. <el-input v-model="form.trainName" />
  28. </el-form-item>
  29. <el-form-item v-if="type==='edit'" label="任务名称" prop="jobName">
  30. <div>{{ form.jobName }}</div>
  31. </el-form-item>
  32. <el-form-item v-if="type==='saveParams' || type==='paramEdit'" label="任务模板名称" prop="paramName">
  33. <el-input id="paramName" v-model="form.paramName" />
  34. </el-form-item>
  35. <el-form-item label="描述" prop="description">
  36. <el-input id="description" v-model="form.description" type="textarea" />
  37. </el-form-item>
  38. <el-divider />
  39. <!--可编辑-->
  40. <template v-if="type!=='saveParams'">
  41. <el-form-item label="选用算法类型" prop="algorithmSource">
  42. <el-radio-group v-model="form.algorithmSource" @change="onAlgorithmSourceChange">
  43. <el-radio
  44. id="algorithm_tab_0"
  45. :label="1"
  46. border
  47. class="mr-0"
  48. >我的算法</el-radio>
  49. <el-radio
  50. id="algorithm_tab_1"
  51. :label="2"
  52. border
  53. >预置算法</el-radio>
  54. </el-radio-group>
  55. </el-form-item>
  56. <el-form-item
  57. ref="algorithmId"
  58. label="选用算法"
  59. prop="algorithmId"
  60. >
  61. <el-select
  62. id="algorithmId"
  63. v-model="form.algorithmId"
  64. v-el-select-load-more="getAlgorithmList"
  65. placeholder="请选择您使用的算法代码"
  66. class="w270"
  67. @change="onAlgorithmChange"
  68. >
  69. <el-option
  70. v-for="item in algorithmIdList"
  71. :key="item.id"
  72. :value="item.id"
  73. :label="item.algorithmName"
  74. />
  75. </el-select>
  76. </el-form-item>
  77. <el-form-item
  78. ref="imageTag"
  79. label="镜像选择"
  80. prop="imageTag"
  81. >
  82. <el-select
  83. id="imageName"
  84. v-model="form.imageName"
  85. placeholder="请选择镜像"
  86. style="width: 190px;"
  87. clearable
  88. @change="getHarborImages"
  89. >
  90. <el-option
  91. v-for="item in harborProjectList"
  92. :key="item"
  93. :label="item"
  94. :value="item"
  95. />
  96. </el-select>
  97. <el-select
  98. id="imageTag"
  99. v-model="form.imageTag"
  100. placeholder="请选择镜像版本"
  101. style="width: 305px;"
  102. clearable
  103. @change="validateField('imageTag')"
  104. >
  105. <el-option
  106. v-for="(item, index) in harborImageList"
  107. :key="index"
  108. :label="item"
  109. :value="item"
  110. />
  111. </el-select>
  112. </el-form-item>
  113. <el-form-item label="加载模型">
  114. <el-switch v-model="form.modelType" :active-value="1" :inactive-value="0" @change="onModelTypeChange"/>
  115. </el-form-item>
  116. <el-form-item v-if="form.modelType" label="选用模型类型">
  117. <el-radio-group v-model="form.modelResource" @change="onModelResourceChange">
  118. <el-radio :label="0" border class="mr-0">我的模型</el-radio>
  119. <el-radio :label="1" border>预训练模型</el-radio>
  120. </el-radio-group>
  121. </el-form-item>
  122. <el-form-item v-if="form.modelType" label="模型选择">
  123. <el-select
  124. id="modelId"
  125. v-model="form.modelId"
  126. placeholder="请选择模型"
  127. style="width: 190px;"
  128. clearable
  129. @change="getModelNames"
  130. >
  131. <el-option
  132. v-for="item in modelNameList"
  133. :key="item.id"
  134. :label="item.name"
  135. :value="item.id"
  136. />
  137. </el-select>
  138. <el-select
  139. v-if="!form.modelResource"
  140. id="modelLoadPathDir"
  141. v-model="form.modelLoadPathDir"
  142. placeholder="请选择模型版本"
  143. style="width: 305px;"
  144. clearable
  145. >
  146. <el-option
  147. v-for="item in modelLoadPathList"
  148. :key="item.id"
  149. :label="item.versionNum"
  150. :value="item.modelAddress"
  151. />
  152. </el-select>
  153. </el-form-item>
  154. <el-form-item ref="trainDataSource" label="训练数据集" prop="dataSourcePath">
  155. <DataSourceSelector
  156. ref="trainDataSourceSelector"
  157. type="train"
  158. :algorithm-usage="form.algorithmUsage"
  159. :data-source-name="form.dataSourceName"
  160. :data-source-path="form.dataSourcePath"
  161. @change="onTrainDataSourceChange"
  162. />
  163. </el-form-item>
  164. <el-form-item
  165. label="验证数据集"
  166. prop="valType"
  167. >
  168. <el-switch
  169. v-model="form.valType"
  170. :active-value="1"
  171. :inactive-value="0"
  172. @change="onVerifyTypeChange"
  173. />
  174. </el-form-item>
  175. <el-form-item v-if="form.valType" ref="verifyDataSource" label="验证数据集" prop="verifyDataSourcePath">
  176. <DataSourceSelector
  177. ref="verifyDataSourceSelector"
  178. type="verify"
  179. :algorithm-usage="form.valAlgorithmUsage"
  180. :data-source-name="form.valDataSourceName"
  181. :data-source-path="form.valDataSourcePath"
  182. @change="onVerifyDataSourceChange"
  183. />
  184. </el-form-item>
  185. <el-form-item ref="runCommand" label="运行命令" prop="runCommand">
  186. <el-input
  187. id="runCommand"
  188. v-model="form.runCommand"
  189. placeholder="例如:python mnist.py"
  190. style="max-width: 500px;"
  191. />
  192. </el-form-item>
  193. <!--运行参数-->
  194. <run-param-form
  195. ref="runParamComp"
  196. :run-param-obj="form.runParams || {}"
  197. prop="runParams"
  198. param-label-width="120px"
  199. class="w120"
  200. @updateRunParams="updateRunParams"
  201. />
  202. <el-divider />
  203. <el-form-item
  204. label="分布式训练"
  205. prop="trainType"
  206. class="mt-10"
  207. >
  208. <el-switch
  209. id="trainType"
  210. v-model="form.trainType"
  211. :active-value="1"
  212. :inactive-value="0"
  213. @change="onTrainTypeChange"
  214. />
  215. </el-form-item>
  216. <el-form-item
  217. v-if="form.trainType"
  218. label="节点数"
  219. prop="resourcesPoolNode"
  220. >
  221. <el-input-number
  222. id="resourcesPoolNode"
  223. v-model="form.resourcesPoolNode"
  224. :min="2"
  225. :max="trainConfig.trainNodeMax"
  226. :step-strictly="true"
  227. />
  228. <el-tooltip effect="dark" content="请确保代码中包含“num_nodes”参数和“node_ips”参数用于接收分布式相关参数" placement="top">
  229. <i class="el-icon-warning-outline primary f18 v-text-top" />
  230. </el-tooltip>
  231. </el-form-item>
  232. <el-form-item label="节点类型" class="is-required">
  233. <el-radio-group
  234. v-model="form.resourcesPoolType"
  235. @change="onResourcesPoolTypeChange"
  236. >
  237. <el-radio
  238. id="resourcesPoolType_tab_0"
  239. :label="0"
  240. border
  241. class="mr-0"
  242. >CPU</el-radio>
  243. <el-radio
  244. id="resourcesPoolType_tab_1"
  245. :label="1"
  246. border
  247. >GPU</el-radio>
  248. </el-radio-group>
  249. <el-tooltip v-if="form.resourcesPoolType" effect="dark" content="后台将自动获取并填充参数 gpu_num_per_node" placement="top">
  250. <i class="el-icon-warning-outline primary f18 v-text-top" />
  251. </el-tooltip>
  252. </el-form-item>
  253. <el-form-item ref="trainJobSpecs" label="节点规格" prop="trainJobSpecsName" class="w270">
  254. <el-select id="trainJobSpecsName" v-model="form.trainJobSpecsName">
  255. <el-option
  256. v-for="spec in specList"
  257. :key="spec.id"
  258. :label="spec.label"
  259. :value="spec.label"
  260. />
  261. </el-select>
  262. <el-tooltip v-if="form.trainType" effect="dark" content="每个节点的节点规格" placement="top">
  263. <i class="el-icon-warning-outline primary f18 v-text-top" />
  264. </el-tooltip>
  265. </el-form-item>
  266. <el-form-item
  267. label="延迟启停"
  268. >
  269. <el-switch
  270. id="delayCreateDelete"
  271. v-model="delayCreateDelete"
  272. @change="onDelayChange"
  273. />
  274. </el-form-item>
  275. <el-form-item
  276. v-if="delayCreateDelete"
  277. label="延迟启动"
  278. prop="delayCreateTime"
  279. >
  280. <el-input-number
  281. id="delayCreateTime"
  282. v-model="form.delayCreateTime"
  283. :min="0"
  284. :max="trainConfig.delayCreateTimeMax"
  285. :step-strictly="true"
  286. />&nbsp;小时
  287. </el-form-item>
  288. <el-form-item
  289. v-if="delayCreateDelete"
  290. label="训练时长上限"
  291. prop="delayDeleteTime"
  292. >
  293. <el-input-number
  294. id="delayDeleteTime"
  295. v-model="form.delayDeleteTime"
  296. :min="0"
  297. :max="trainConfig.delayDeleteTimeMax"
  298. :step-strictly="true"
  299. />&nbsp;小时
  300. <el-tooltip effect="dark" content="选择 0 表示不限制训练时长" placement="top">
  301. <i class="el-icon-warning-outline primary f18 v-text-top" />
  302. </el-tooltip>
  303. </el-form-item>
  304. <el-form-item label="运行命令预览" prop="preview">
  305. <div class="param">
  306. {{ preview }}
  307. </div>
  308. </el-form-item>
  309. </template>
  310. <!--不可编辑-->
  311. <template v-if="type==='saveParams'">
  312. <el-form-item label="选用算法类型">
  313. {{ form.algorithmSource === 2 ? '预置算法' : '我的算法' }}
  314. </el-form-item>
  315. <el-form-item label="选用算法">
  316. {{ form.algorithmName }}
  317. </el-form-item>
  318. <el-form-item label="镜像选择">
  319. {{ form.imageName }}
  320. </el-form-item>
  321. <el-form-item label="模型选择">
  322. {{ form.modelName }}
  323. </el-form-item>
  324. <el-form-item label="训练数据集">
  325. {{ form.dataSourceName }}
  326. </el-form-item>
  327. <el-form-item label="验证数据集">
  328. {{ form.valDataSourceName }}
  329. </el-form-item>
  330. <el-form-item label="运行命令">
  331. {{ form.runCommand }}
  332. </el-form-item>
  333. <el-form-item label="运行参数">
  334. <span
  335. v-for="key of Object.keys(form.runParams || {})"
  336. :key="key"
  337. >--{{ key }}={{ form.runParams[key] }} </span>
  338. </el-form-item>
  339. <el-form-item label="分布式训练">
  340. {{ form.trainType === 1 ? '是' : '否' }}
  341. </el-form-item>
  342. <el-form-item v-if="form.trainType" label="节点数">
  343. {{ form.resourcesPoolNode }}
  344. </el-form-item>
  345. <el-form-item label="延迟启停">
  346. {{ delayCreateDelete ? '是' : '否' }}
  347. </el-form-item>
  348. <el-form-item v-if="delayCreateDelete" label="延迟启动">
  349. {{ form.delayCreateTime }}&nbsp;小时
  350. </el-form-item>
  351. <el-form-item v-if="delayCreateDelete" label="延迟停止">
  352. {{ form.delayDeleteTime }}&nbsp;小时
  353. </el-form-item>
  354. <el-form-item label="节点类型">
  355. {{ form.resourcesPoolNode }}
  356. </el-form-item>
  357. <el-form-item label="节点规格">
  358. {{ formSpecs && formSpecs.label }}
  359. </el-form-item>
  360. <el-form-item label="运行命令预览">
  361. <div class="param">
  362. {{ preview }}
  363. </div>
  364. </el-form-item>
  365. </template>
  366. </el-form>
  367. </div>
  368. </template>
  369. <script>
  370. import { validateNameWithHyphen } from '@/utils';
  371. import { list as getAlgorithmList } from '@/api/algorithm/algorithm';
  372. import { harborProjectNames, harborImageNames } from '@/api/system/harbor';
  373. import { list as getModelName } from '@/api/model/model';
  374. import { list as getModelTag } from '@/api/model/modelVersion';
  375. import { trainConfig } from '@/config';
  376. import RunParamForm from './runParamForm';
  377. import DataSourceSelector from './dataSourceSelector';
  378. const defaultForm = {
  379. id: null, // 用于编辑训练任务时, 表单传递 jobId
  380. trainName: '',
  381. jobName: '', // 用于编辑训练任务时, 表单展示 jobName
  382. paramName: '',
  383. description: '',
  384. algorithmSource: 1,
  385. algorithmId: null,
  386. algorithmName: null,
  387. algorithmUsage: null,
  388. valAlgorithmUsage: null,
  389. imageTag: null,
  390. imageName: null,
  391. dataSourceName: null,
  392. dataSourcePath: null,
  393. valDataSourceName: null,
  394. valDataSourcePath: null,
  395. runCommand: '',
  396. runParams: {},
  397. trainType: 0,
  398. valType: 0,
  399. resourcesPoolNode: 1,
  400. resourcesPoolType: 0,
  401. trainJobSpecsName: null,
  402. outPath: '/home/result/',
  403. logPath: '/home/log/',
  404. // 延迟启停相关参数
  405. delayCreateTime: 0,
  406. delayDeleteTime: 0,
  407. modelType: 0,
  408. modelResource: 0,
  409. modelId: null,
  410. modelLoadPathDir: null,
  411. modelName: null,
  412. };
  413. export default {
  414. name: 'JobForm',
  415. dicts: ['cpu_specs', 'gpu_specs'],
  416. components: { RunParamForm, DataSourceSelector },
  417. props: {
  418. type: {
  419. type: String,
  420. default: 'add', // add: 新增训练任务; paramsAdd: 任务参数创建训练任务; algoAdd: 算法创建训练任务; edit: 修改训练任务; saveParams: 保存训练参数模板; paramEdit: 修改训练参数模板。
  421. },
  422. widthPercent: {
  423. type: Number,
  424. default: 60,
  425. },
  426. },
  427. data() {
  428. return {
  429. algorithmIdList: [],
  430. harborProjectList: [],
  431. harborImageList: [],
  432. modelNameList: [],
  433. modelLoadPathList: [],
  434. noMoreLoadAlg: false,
  435. algLoading: false,
  436. currentAlgPage: 1,
  437. algPageSize: 1000,
  438. runParamObj: {}, // 该对象用于提交训练
  439. dictReady: false,
  440. delayCreateDelete: false,
  441. selectedAlgorithm: null,
  442. trainConfig,
  443. form: { ...defaultForm },
  444. rules: {
  445. trainName: [
  446. { required: true, message: '请输入任务名称', trigger: 'blur' },
  447. { max: 32, message: '长度控制在32个字符', trigger: 'blur' },
  448. { validator: validateNameWithHyphen, trigger: ['blur', 'change'] },
  449. ],
  450. paramName: [
  451. { required: true, message: '请输入任务参数名称', trigger: 'blur' },
  452. { max: 32, message: '长度控制在32个字符', trigger: 'blur' },
  453. { validator: validateNameWithHyphen, trigger: ['blur', 'change'] },
  454. ],
  455. algorithmSource: [
  456. { required: true, message: '请选择算法', trigger: 'change' },
  457. ],
  458. algorithmId: [
  459. { required: true, message: '请选择算法', trigger: 'manual' },
  460. ],
  461. imageTag: [
  462. { required: true, message: '请选择镜像', trigger: 'manual' },
  463. ],
  464. dataSourcePath: [
  465. { required: true, message: '请选择数据集', trigger: 'manual' },
  466. ],
  467. trainJobSpecsName: [
  468. { required: true, message: '请选择节点规格', trigger: 'change' },
  469. ],
  470. runCommand: [
  471. { required: true, message: '请输入运行命令', trigger: ['blur', 'change'] },
  472. ],
  473. },
  474. };
  475. },
  476. computed: {
  477. formSpecs() {
  478. return this.specList.find(spec => spec.label === this.form.trainJobSpecsName);
  479. },
  480. specList() {
  481. switch(this.form.resourcesPoolType) {
  482. case 0:
  483. return this.dict.cpu_specs;
  484. case 1:
  485. return this.dict.gpu_specs;
  486. default:
  487. return [];
  488. }
  489. },
  490. preview() {
  491. let str = this.form.runCommand;
  492. for(const key of Object.keys(this.runParamObj)) {
  493. str += ` --${key}=${this.runParamObj[key]}`;
  494. }
  495. if (this.selectedAlgorithm) {
  496. str += this.selectedAlgorithm.isTrainLog? ' --train_log=/workspace/log' : '';
  497. str += this.selectedAlgorithm.isTrainOut? ' --train_out=/workspace/out' : '';
  498. str += this.selectedAlgorithm.isVisualizedLog? ' --train_visualized_log=/workspace/visualizedlog' : '';
  499. str += ' --data_url=/dataset';
  500. }
  501. str += this.form.valDataSourceName && this.form.valDataSourcePath ? ' --val_data_url=/valdataset' : '';
  502. str += this.form.modelId && this.form.modelLoadPathDir ? ' --model_load_dir=/modeldir' : '';
  503. if (this.form.resourcesPoolType) {
  504. // eslint-disable-next-line no-template-curly-in-string
  505. str += ' --gpu_num_per_node=${gpu_num}';
  506. }
  507. return str;
  508. },
  509. },
  510. mounted() {
  511. this.$on('dictReady', () => { this.dictReady = true; });
  512. },
  513. methods: {
  514. initForm(form) {
  515. const newForm = form || {};
  516. Object.keys(this.form).forEach(item => { newForm[item] && (this.form[item] = newForm[item]); });
  517. setTimeout(() => {
  518. this.delayCreateDelete = (this.form.delayCreateTime !== 0) && (this.form.delayDeleteTime !== 0);
  519. this.getAlgorithmList();
  520. if (this.type !== 'saveParams') {
  521. this.getHarborProjects().then(() => {
  522. this.resetProject();
  523. });
  524. this.getModelNames(false);
  525. this.$refs.trainDataSourceSelector.updateAlgorithmUsage(this.form.algorithmUsage, true);
  526. this.form.valType && this.$refs.verifyDataSourceSelector.updateAlgorithmUsage(this.form.valAlgorithmUsage, true);
  527. }
  528. if (this.dictReady) {
  529. this.onResourcesPoolTypeChange((this.type !== 'add') && (this.type !== 'algoAdd'));
  530. } else {
  531. this.$on('dictReady', () => this.onResourcesPoolTypeChange((this.type !== 'add') && (this.type !== 'algoAdd')));
  532. }
  533. // runParamObj 初始值为 form.runParams
  534. this.runParamObj = {...this.form.runParams} || {};
  535. this.clearValidate();
  536. }, 0);
  537. },
  538. validate(...args) {
  539. this.$refs.form.validate.apply(this, args);
  540. },
  541. clearValidate(...args) {
  542. this.$refs.form.clearValidate.apply(this, args);
  543. },
  544. validateField(field) {
  545. this.$refs[field].validate('manual');
  546. },
  547. clearFieldValidate(field) {
  548. this.$refs[field].clearValidate();
  549. },
  550. updateRunParams(params) {
  551. this.runParamObj = params;
  552. },
  553. save() {
  554. if (this.loading) {
  555. return;
  556. }
  557. // 先将字符串模式转换为键值对模式
  558. if (this.type !== 'saveParams' && this.$refs.runParamComp.paramsMode === 2) {
  559. this.$refs.runParamComp.convertArgsToPairs();
  560. }
  561. const runParamsValid = this.type === 'saveParams' || this.$refs.runParamComp.validate();
  562. if (runParamsValid) {
  563. this.$refs.form.validate(async valid => {
  564. if (valid) {
  565. const params = {...this.form};
  566. params.runParams = {...this.runParamObj};
  567. params.trainJobSpecsInfo = this.formSpecs.value;
  568. delete params.modelName; // modelName只用来展示,不作为提交参数
  569. // 请求交互都不放在组件完成
  570. this.$emit('getForm', params);
  571. } else {
  572. this.$message({
  573. message: '请仔细检查任务参数',
  574. type: 'warning',
  575. });
  576. }
  577. });
  578. } else {
  579. this.$message({
  580. message: '运行参数不合法',
  581. type: 'warning',
  582. });
  583. }
  584. },
  585. // 镜像项目为空时选择默认项目
  586. resetProject() {
  587. if (!this.form.imageName) {
  588. if (this.harborProjectList.some(project => project === 'oneflow')) {
  589. this.form.imageName = 'oneflow';
  590. } else if (this.harborProjectList.length) {
  591. Object.assign(this.form, { imageName: this.harborProjectList[0] });
  592. } else {
  593. this.$message.warning('镜像项目列表为空');
  594. return;
  595. }
  596. this.getHarborImages();
  597. }
  598. },
  599. reset() {
  600. this.$refs.trainDataSourceSelector.reset();
  601. this.form = { ...defaultForm };
  602. this.runParamObj = {};
  603. this.selectedAlgorithm = null;
  604. this.delayCreateDelete = false;
  605. this.$message({
  606. message: '数据已重置',
  607. type: 'success',
  608. });
  609. setTimeout(() => {
  610. this.onResourcesPoolTypeChange();
  611. this.resetProject();
  612. this.$refs.form.clearValidate();
  613. this.$refs.runParamComp.reset();
  614. }, 0);
  615. },
  616. async getHarborProjects() {
  617. this.harborProjectList = await harborProjectNames();
  618. if (this.form.imageName && !this.harborProjectList.some(project => project === this.form.imageName)) {
  619. this.$message.warning('该训练原有的运行项目不存在,请重新选择');
  620. this.form.imageName = null;
  621. this.form.imageTag = null;
  622. return;
  623. }
  624. this.form.imageName && await this.getHarborImages(true);
  625. if (this.form.imageTag && !this.harborImageList.some(image => image === this.form.imageTag)) {
  626. this.$message.warning('该训练原有的运行镜像不存在,请重新选择');
  627. this.form.imageTag = null;
  628. }
  629. },
  630. getHarborImages(saveImageName = false) {
  631. if (saveImageName !== true) {
  632. this.form.imageTag = null;
  633. }
  634. if (!this.form.imageName) {
  635. this.harborImageList = [];
  636. return;
  637. }
  638. return harborImageNames({ imageName: this.form.imageName })
  639. .then(res => {
  640. this.harborImageList = res;
  641. });
  642. },
  643. async getModelNames(saveModel = true) {
  644. this.modelNameList = await getModelName({ modelResource: this.form.modelResource, filter: true });
  645. if (!this.form.modelId) [this.modelLoadPathList, this.form.modelLoadPathDir] = [[], null];
  646. (this.form.modelId && !this.form.modelResource) && this.modelLoadPath(saveModel);
  647. },
  648. async modelLoadPath(create) {
  649. if (create) {
  650. this.form.modelLoadPathDir = null;
  651. };
  652. const data = await getModelTag({ parentId: this.form.modelId });
  653. this.modelLoadPathList = data.result;
  654. },
  655. onModelResourceChange() {
  656. this.form.modelId = this.form.modelLoadPathDir = null;
  657. this.getModelNames();
  658. },
  659. onModelTypeChange() {
  660. if (this.form.modelType === 0 ) {
  661. this.form = Object.assign(this.form, {
  662. modelResource: 0,
  663. modelId: null,
  664. modelLoadPathDir: null,
  665. });
  666. };
  667. },
  668. getAlgorithmList() {
  669. if (this.noMoreLoadAlg || this.algLoading) {
  670. return;
  671. }
  672. this.algLoading = true;
  673. const params = {
  674. algorithmSource: Number(this.form.algorithmSource || 1),
  675. current: this.currentAlgPage,
  676. size: this.algPageSize,
  677. };
  678. getAlgorithmList(params).then(res => {
  679. this.algorithmIdList = this.algorithmIdList.concat(res.result);
  680. this.currentAlgPage += 1;
  681. this.algLoading = false;
  682. if (res.result.length < this.algPageSize) {
  683. this.noMoreLoadAlg = true;
  684. }
  685. if (this.form.algorithmId) {
  686. this.selectedAlgorithm = this.algorithmIdList.find(item => item.id === this.form.algorithmId);
  687. if (!this.selectedAlgorithm) {
  688. this.$message.warning('原有算法不存在,请重新选择');
  689. this.form.algorithmId = null;
  690. }
  691. }
  692. }).finally(() => {
  693. this.algLoading = false;
  694. });
  695. },
  696. onResourcesPoolTypeChange(keepSpec = false) {
  697. // 当没有显式指定保留节点规格时,选择规格列表第一个选项
  698. if (keepSpec !== true && this.specList.length) {
  699. this.form.trainJobSpecsName = this.specList[0].label;
  700. }
  701. },
  702. onTrainDataSourceChange(dataSourceResult) {
  703. this.form.dataSourceName = dataSourceResult.dataSourceName;
  704. this.form.dataSourcePath = dataSourceResult.dataSourcePath;
  705. dataSourceResult.dataSourcePath && this.$refs.trainDataSource.validate('manual');
  706. // 如果在运行参数中包含了 image_counts 字段,则自动把数据集图片数量填充至该字段。
  707. this.$refs.runParamComp.updateParam('image_counts', dataSourceResult.imageCounts);
  708. },
  709. onVerifyDataSourceChange(result) {
  710. this.form.valDataSourceName = result.dataSourceName;
  711. this.form.valDataSourcePath = result.dataSourcePath;
  712. },
  713. async onAlgorithmChange(id) {
  714. // 选用算法变更时,需要对自动填充的表单项进行验证
  715. this.validateField('algorithmId');
  716. // 选用算法变更时,需要同步算法的算法用途、运行项目、运行镜像、运行命令、运行参数
  717. const algorithm = this.algorithmIdList.find(i => i.id === id);
  718. this.selectedAlgorithm = algorithm;
  719. this.form.algorithmUsage = algorithm?.algorithmUsage || null;
  720. this.form.valAlgorithmUsage = algorithm?.algorithmUsage || null;
  721. this.$refs.trainDataSourceSelector.updateAlgorithmUsage(this.form.algorithmUsage);
  722. this.form.valType && this.$refs.verifyDataSourceSelector.updateAlgorithmUsage(this.form.valAlgorithmUsage);
  723. this.form.runCommand = algorithm?.runCommand || '';
  724. this.form.runParams = algorithm?.runParams || {};
  725. this.runParamObj = {...this.form.runParams};
  726. this.form.imageName = algorithm?.imageName;
  727. this.$nextTick(() => {
  728. this.clearFieldValidate('runCommand');
  729. });
  730. if (this.form.imageName && !this.harborProjectList.some(project => project === this.form.imageName)) {
  731. this.$message.warning('算法选择的运行项目不存在,请重新选择');
  732. this.form.imageName = null;
  733. this.form.imageTag = null;
  734. return;
  735. }
  736. this.form.imageName && await this.getHarborImages(true);
  737. this.form.imageTag = algorithm?.imageTag;
  738. if (this.form.imageTag && !this.harborImageList.some(image => image === this.form.imageTag)) {
  739. this.$message.warning('算法选择的运行镜像不存在,请重新选择');
  740. this.form.imageTag = null;
  741. return;
  742. }
  743. if (this.form.imageTag) {
  744. this.validateField('imageTag');
  745. }
  746. this.resetProject();
  747. },
  748. onDelayChange(isDelay) {
  749. if (!isDelay) {
  750. this.form.delayCreateTime = 0;
  751. this.form.delayDeleteTime = 0;
  752. }
  753. },
  754. onVerifyTypeChange() {
  755. if (this.form.valType === 0 ) {
  756. this.form = Object.assign(this.form, {
  757. valDataSourceName: null,
  758. valDataSourcePath: null,
  759. });
  760. this.$refs.verifyDataSourceSelector.reset();
  761. } else {
  762. // 打开训练数据集时获取相应值
  763. this.$nextTick(() => {
  764. this.$refs.verifyDataSourceSelector.updateAlgorithmUsage(this.form.valAlgorithmUsage, false);
  765. });
  766. }
  767. },
  768. async onAlgorithmSourceChange() {
  769. // 算法类型更改之后,需要清空下方表单
  770. this.algorithmIdList = [];
  771. this.currentAlgPage = 1;
  772. this.noMoreLoadAlg = false;
  773. this.selectedAlgorithm = null;
  774. this.form = Object.assign(this.form, {
  775. algorithmId: null,
  776. algorithmUsage: null,
  777. dataSourceName: null,
  778. dataSourcePath: null,
  779. valAlgorithmUsage: null,
  780. valDataSourceName: null,
  781. valDataSourcePath: null,
  782. imageTag: null,
  783. imageName: null,
  784. runCommand: '',
  785. resourcesPoolType: 0,
  786. valType: 0,
  787. runParams: {},
  788. modelType: 0,
  789. modelResource: 0,
  790. modelId: null,
  791. modelLoadPathDir: null,
  792. });
  793. this.getAlgorithmList();
  794. this.$refs.trainDataSourceSelector.reset();
  795. // 切换算法时去获取相应内容
  796. this.$refs.trainDataSourceSelector.updateAlgorithmUsage(null);
  797. this.runParamObj = {};
  798. this.$nextTick(() => {
  799. this.clearFieldValidate('runCommand');
  800. this.clearFieldValidate('trainJobSpecs');
  801. });
  802. this.$refs.runParamComp.reset();
  803. this.harborImageList = [];
  804. this.resetProject();
  805. this.onResourcesPoolTypeChange();
  806. },
  807. onTrainTypeChange(trainType) {
  808. this.form.resourcesPoolNode = trainType === 0 ? 1 : 2;
  809. },
  810. },
  811. };
  812. </script>
  813. <style lang="scss" scoped>
  814. ::v-deep.w270 {
  815. .el-input {
  816. width: 270px;
  817. }
  818. }
  819. ::v-deep.el-input-number {
  820. width: 270px;
  821. .el-input-number__increase,
  822. .el-input-number__decrease {
  823. width: 70px;
  824. }
  825. }
  826. .el-radio.is-bordered {
  827. width: 130px;
  828. height: 35px;
  829. padding: 10px 0;
  830. text-align: center;
  831. }
  832. .param {
  833. min-height: 80px;
  834. padding: 0 10px;
  835. line-height: 25px;
  836. color: rgb(204, 204, 204);
  837. background: rgb(30, 30, 30);
  838. border-radius: 5px;
  839. }
  840. </style>

一站式算法开发平台、高性能分布式深度学习框架、先进算法模型库、视觉模型炼知平台、数据可视化分析平台等一系列平台及工具,在模型高效分布式训练、数据处理和可视分析、模型炼知和轻量化等技术上形成独特优势,目前已在产学研等各领域近千家单位及个人提供AI应用赋能

Contributors (1)