毕业设计:Vue3+FastApi+Python+Neo4j实现主题知识图谱网页应用——数据处理:数据标注

简介

完整项目资源链接:https://download.csdn.net/download/m0_46573428/87796553

项目详细信息请看:毕业设计:Vue3+FastApi+Python+Neo4j实现主题知识图谱网页应用——前言_人工智能技术小白修炼手册的博客-CSDN博客

数据标注部分涉及的主要功能是与标注工具相关的数据导入、数据浏览和数据保存等。具体而言,包括以下几个部分:

  1. 数据导入部分:包括数据预处理和数据存储两个阶段。数据预处理阶段调用了多种函数来对原始数据进行清洗和转换,以便在页面加载时更好地展示。其中包括读取Excel文件并将其转换为Json格式的数据,以及将Json格式的数据转换为特定格式的节点图数据。数据存储阶段则针对每个数据库创建DataFiles文件,将Json格式的数据写入到该文件中。

  2. 数据浏览部分:通过API接口实现,可以在前端页面上实时查询数据库的基本信息、所有数据集的列表以及每个数据集的详细信息。具体而言,可以查看数据集的总数、已标注数据的数量和数据集的名称和ID等信息,还可以查看每个数据集下的数据节点列表,包括数据节点的文本及其所对应的节点和关系信息。

  3. 数据保存部分:用于保存前端标注的数据。具体而言,函数可以将前端传来的标注数据按照一页一页地保存,每次只保存一个段落的数据。如果保存的数据与数据集中的文本相似,则会更新该文本的“nodes”和“rels”数据,并将更新后的数据再次写入到数据文件中。

 

主要代码

1. HTML部分

HTML代码渲染出了一个具有栏目布局的页面,包括左侧的属性栏、中间的标注区和右侧的图片展示区等,其中各个部分的具体功能与作用如下:

  • 左侧属性栏:提供可拖动调整宽度的按钮和标签/属性列表,用户可以点击标签/属性名称将其添加到实体属性编辑区。

  • 中间标注区:数据节点展示和编辑区,包括节点属性编辑区和关系列表,用于观察和编辑实体节点、关系和属性等,并且支持翻页查看和数据保存功能。

  • 右侧图片展示区:用于展示相关实体或关系的图片信息。

2. JavaScript部分

JavaScript部分为Vue.js组件中的具体实现部分,其中包含了处理用户交互的各种函数,以及一些初始化和配置的代码。这些函数大致可分为以下几类:

  • 数据标注:负责在节点展示区中添加和编辑实体节点、关系和属性等, 具体函数包括addNode()、addRels()和addProperties()等。

  • 数据删除:可删除指定节点或关系的属性或整个节点或关系等,具体函数包括discardPro()和discardRel()等。

  • 页面翻页:用于在标注区域之间进行翻页操作,具体函数有goPrev()和goNext()。

  • 数据保存:将标注结果序列化并上传到服务器保存,具体函数为Save()。

  • 数据查询:从服务器获取已有的标注数据集,具体函数为browse()。

其中所有的方法和函数都通过Vue.js组件实例中的data对象保存了需要使用的数据,成员变量包括labels和properties(标签和属性名称列表)、id(数据库中数据集的ID)、index(当前数据节点的下标),以及实际存储数据的nodes和rels。

JS部分方法说明

以下是对上述提到的JavaScript中的函数进行展开介绍:

1. addNode()

addNode()函数用于向当前数据集的节点列表中添加一个新的实体节点,并将该节点的属性设置为空对象。具体实现如下:

addNode: function (text) {
  this.id++;
  var newNode = {
    名称: text,
  };
  this.dataAll[0][this.index].nodes["node" + this.id.toString()] = newNode;
},

函数会将新节点的名称设为text,并在dataAll的第一个元素中的nodes对象中新增一个node,命名格式为“node”+id。

2. addProperties()

addProperties()函数用于在选定的实体节点上添加一个新的属性,并将该属性默认值设为空字符串。具体实现如下:

addProperties: function (propName) {
  var propsIndex = this.tmpIndex + 1;
  this.dataAll[0][this.index].nodes[
    "node" + propsIndex.toString()
  ][propName] = "";
},

函数会在实际存储数据的节点列表中找到所选的节点,对其新增一个键值对属性,键名为propName,初始值为空字符串。

3. addRels()

addRels()函数用于向当前数据集的关系列表中添加一条关系记录,并在关系记录中填入起始节点、结束节点和关系名称等信息。具体实现如下:

addRels: function () {
  this.id++;
  var rel = {
    start: "",
    end: "",
    rel_name: "",
  };
  this.dataAll[0][this.index].rels["rel" + this.id.toString()] = rel;
},

函数首先将id值加1,然后创建一条新的关系记录,其中包含了起始节点、结束节点和关系名称等信息,最后在实际存储数据的rels对象中新增一条关系记录。

4. discardPro()

discardPro()函数用于删除指定节点上的属性键名及其对应键值。具体实现如下:

discardPro: function (i, ikey) {
  i = i + 1;
  delete this.dataAll[0][this.index].nodes["node" + i.toString()][ikey];
},

函数通过接收两个参数,分别为需要删除属性的节点所在的下标i,以及需要删除的属性键名ikey。具体逻辑为找到需要删除的节点,根据传入的i变量取出该节点,再据调用时传入的ikey变量删除该节点的参数。

5. discardRel()

discardRel()函数用于删除当前数据节点中某一条关系记录,究竟要删除第几条记录由参数i决定。具体代码如下:

discardRel: function (i) {
  i = i + 1;
  delete this.dataAll[0][this.index].rels["rel" + i.toString()];
},

函数通过将传入的参数i加1后作为被删关系的下标,再据此删除dataAll中rels的相应关系记录。

6. goPrev()

goPrev()函数用于在多组数据之间进行翻页操作,向前翻页。具体实现如下:


goPrev: function () {
  if (this.index > 0) this.index--;
},

函数检查当前数据节点的下标index是否大于0,如果是则将其减1,表示向前进入上一组数据。

7. goNext()

goNext()函数用于在多组数据之间进行翻页操作,向后翻页。具体实现如下:


goNext: function () {
  if (this.index + 1 < this.dataAll[0].length) this.index++;
},

函数检查当前数据节点的下标加1后是否小于dataAll中元素的个数,如果是,则将其加1,表示向后进入下一组数据。

8. Save()

Save()函数用于保存用户标注的数据,将当前数据集序列化为JSON格式并上传到服务器进行保存。具体实现如下:


Save: function () {
  var json_body = JSON.stringify(this.dataAll[0]);
  axios.get("http://localhost:8080/save", {
    params: {
      id: this.id,
      text: this.dataAll[0][this.index].text,
      data_text: json_body,
    },
  });
},

函数首先调用JSON.stringify()方法将当前标注完成的数据序列化为JSON字符串,然后结合axios发送请求将数据上传到服务器上的save接口。其中params对象中包含了需要上传的数据,即当前数据集的id、节点名称和标注内容等。

9. browse()

browse()函数用于从服务器查询已有的数据集,将查询到的数据集更新到dataAll变量中。

返回的数据类型为JSON格式,其中data属性包含了服务器返回的数据,如下示例:

{"status": "success","data": [{"id": 1,"text": "数据集1","nodes": {"node1": { "名称": "实体1", "属性1": "属性值1", "属性2": "属性值2" },"node2": { "名称": "实体2", "属性1": "", "属性3": "属性值3" }},"rels": {"rel1": { "start": "node1", "end": "node2", "rel_name": "关系1" }}},{"id": 2,"text": "数据集2","nodes": {"node1": { "名称": "实体1", "属性1": "属性值1", "属性3": "" },"node2": { "名称": "实体2", "属性2": "属性值2", "属性3": "属性值3" }},"rels": {"rel1": { "start": "node1", "end": "node2", "rel_name": "关系1" },"rel2": { "start": "node2", "end": "node1", "rel_name": "关系2" }}}]}

函数通过axios向服务器发送请求,获取存储在服务器上的JSON格式的数据集信息。然后将dataAll第一个元素(即数据节点列表)清空,并根据响应数据中的id、text、nodes和rels等属性更新dataAll中的数据节点列表。

10. 其他函数

除了上述的核心函数,JavaScript代码中还包含了一些其他的函数,如根据id获取特定数据集信息的get_data()函数、初始化Vue组件实例的init()函数和映射单个节点到编辑区的mapNode()函数等。这些函数在整个组件中起到辅助和初始化配置的作用。

前端

Html

<template>
  <div>
    <div id="main">
      <textarea
        @mouseup="mouseSelect()"
        class="text"
        v-model="dataAll[0][index].text"
      ></textarea>
      <div class="four">
        <div>
          <h1>标签标注</h1>
          <div style="height: 180px; overflow: scroll; margin-top: 10px">
            <ul>
              <li
                @dblclick="addNode(item)"
                v-for="(item, index) in labels"
                :key="index"
              >
                {{ item }}
              </li>
            </ul>
          </div>
        </div>
        <div>
          <h1>属性标注</h1>
          <div style="height: 180px; overflow: scroll; margin-top: 10px">
            <ul>
              <li
                @dblclick="addProperties(item)"
                v-for="(item, index) in properties"
                :key="index"
              >
                {{ item }}
              </li>
            </ul>
          </div>
        </div>
        <div>
          <h1>实体</h1>

          <div style="height: 180px; overflow: scroll; margin-top: 10px">
            <ul class="ShiTi">
              <li
                v-for="(item, key, index) in dataAll[0][index].nodes"
                :key="index"
              >
                <p
                  @dblclick="discardNode(index)"
                  @click="getTmpIndex(index)"
                  :class="index == tmpIndex ? 'selected' : ''"
                >
                  {{ item.名称 }}
                </p>
                <p
                  @dblclick="discardPro(index, ikeys)"
                  v-for="(iitem, ikeys, iindex) in item"
                  :key="iindex"
                >
                  <span> {{ ikeys }}:</span>
                  <span>{{ iitem }}</span>
                </p>
              </li>
            </ul>
          </div>
        </div>
        <div>
          <h1 style="margin-bottom: 10px">关系</h1>
          <select v-model="relType">
            <option value="生产研发">生产研发</option>
            <option value="属于">属于</option>
            <option value="产国">产国</option>
          </select>
          <span class="add" @click="addRels()">新增</span>
          <div style="height: 145px; overflow: scroll; margin-top: 10px">
            <ul class="GuanXi">
              <li
                v-for="(item, key, index) in dataAll[0][index].rels"
                :key="index"
              >
                <input v-model="item.start" />
                <!-- <p>{{ dataAll[0][index].rels[key].start }}</p> -->
                <!-- <input :value="dataAll[0][index].rels[key].start" /> -->
                <p @dblclick="discardRel(index)">{{ item.rel_name }}</p>
                <!-- <input :value="dataAll[0][index].rels[key]['end']" /> -->
                <input v-model="item.end" />
              </li>
            </ul>
          </div>
        </div>
      </div>
      <img
        @click="goPrev()"
        style="cursor: pointer"
        src="../../assets/left.svg"
      />
      <img
        @click="goNext()"
        style="cursor: pointer"
        src="../../assets/right.svg"
      />
      <button @click="Save()">保&nbsp;&nbsp;&nbsp;&nbsp; 存</button>
    </div>
  </div>
</template>

Script

<script>
export default {
  name: "Label",
  props: {},

  data() {
    return {
      tmpText: "",
      labels: [
        "XXX",
        "XXX",
        "XXX",
        "XXX",
        "XXX",
        "XXX",

      ],
      properties: [
        "XXX",
        "XXX",
        "XXX",
        "XXX",
        "XXX",
        "XXX",
      ],

      nodes: [],
      rels: [],
      relType: "生产研发",
      id: 1,
      dataAll: [[{ text: "ceshi", nodes: { node1: {} }, rels: { rel1: {} } }]],
      index: 0,
      tmpIndex: -1,
      dataText: "",
      dataNodes: {},
      dataRels: {},
    };
  },
  methods: {
    // 数据标注:双击删除属性
    discardPro: function (i, ikey) {
      i = i + 1;

      delete this.dataAll[0][this.index].nodes["node" + i.toString()][ikey];
    },
    // 数据标注:双击删除关系
    discardRel: function (i) {
      i = i + 1;
      console.log(i);
      delete this.dataAll[0][this.index].rels["rel" + i.toString()];
      console.log(this.dataAll[0][this.index].rels);
      var count = 1;
      var tmp = this.dataAll[0][this.index].rels;
      this.dataAll[0][this.index].rels = {};
      for (let key in tmp) {
        this.dataAll[0][this.index].rels["rel" + count.toString()] = tmp[key];
        count++;
      };
      console.log(this.dataAll[0][this.index].rels);
    },
    // 数据标注:双击删除实体
    discardNode: function (i) {
      i = i + 1;

      delete this.dataAll[0][this.index].nodes["node" + i.toString()];
      var count = 1;
      var tmp = {};
      tmp = this.dataAll[0][this.index].nodes;
      this.dataAll[0][this.index].nodes = {};
      for (let key in tmp) {
        this.dataAll[0][this.index].nodes["node" + count.toString()] = tmp[key];
        count++;
      }
      console.log("删除了");
      console.log(this.dataAll[0][this.index].nodes);
    },
    // 数据标注:双击增加实体
    addNode: function (item) {
      console.log(this.dataAll[0][this.index]);
      console.log(Object.keys(this.dataAll[0][this.index].nodes).length);
      var nodesLength =
        Object.keys(this.dataAll[0][this.index].nodes).length + 1;

      this.dataAll[0][this.index].nodes["node" + nodesLength.toString()] = {
        label: [item],
        名称: this.tmpText,
      };
    },

    // 数据标注:增加关系
    addRels: function () {
      this.rels.push();
      var relsLength = Object.keys(this.dataAll[0][this.index].rels).length + 1;
      console.log(this.index, relsLength, this.relType);
      this.dataAll[0][this.index].rels["rel" + relsLength.toString()] = {
        start: "",
        end: "",
        rel_name: this.relType,
      };
    },

    // 数据标注:双击增加属性
    addProperties: function (item) {
      var i = this.tmpIndex + 1;
      this.dataAll[0][this.index].nodes["node" + i.toString()][item] =
        this.tmpText;

      console.log(
        item,
        this.tmpText,
        this.dataAll[0][this.index].nodes["node" + i.toString()]
      );
    },

    getTmpIndex: function (i) {
      if (this.tmpIndex == i) {
        this.tmpIndex = -1;
      } else if (this.tmpIndex != i) {
        this.tmpIndex = i;
      }
    },

    // 数据标注:点击前翻
    goPrev: function () {
      this.index--;
      if (this.index < 0) {
        this.index = this.dataAll[0].length - 1;
      }
      var count = 1;
      var tmp = {};
      tmp = this.dataAll[0][this.index].nodes;
      this.dataAll[0][this.index].nodes = {};
      for (let key in tmp) {
        this.dataAll[0][this.index].nodes["node" + count.toString()] = tmp[key];
        count++;
      };
      console.log(this.index);
    },

    // 数据标注:点击后翻
    goNext: function () {
      this.index++;
      if (this.index > this.dataAll[0].length - 1) {
        this.index = 0;
      }
      var count = 1;
      var tmp = {};
      tmp = this.dataAll[0][this.index].nodes;
      this.dataAll[0][this.index].nodes = {};
      for (let key in tmp) {
        this.dataAll[0][this.index].nodes["node" + count.toString()] = tmp[key];
        count++;
      };
      console.log(this.dataAll[0].length);
      console.log(this.index);
    },
    mouseSelect: function () {
      this.tmpText = window.getSelection().toString();
      console.log(this.tmpText);
    },
    browse: function () {
      this.axios
        .get("http://localhost:8000/Browse?database_id=" + this.id)
        .then((res) => {
          if (res.data == "数据为空") {
            alert("数据集为空");
          }
          console.log("Label的:", res.data);
          this.dataAll = res.data;
          var count = 1;
          var tmp = {};
          tmp = this.dataAll[0][this.index].nodes;
          this.dataAll[0][this.index].nodes = {};
          for (let key in tmp) {
            this.dataAll[0][this.index].nodes["node" + count.toString()] =
              tmp[key];
            count++;
          }
        })
        .catch((error) => {
          console.log(error);
          alert("Browse 失败");
        });
    },
    // 数据处理:数据保存
    Save: function () {
      console.log(this.dataAll);

      var data = JSON.stringify(this.dataAll);
      console.log(data);
      this.axios
        .get(
          "http://localhost:8000/DataSave?dataInput=" +
            data +
            "&database_id=" +
            this.id
        )
        .then((res) => {
          alert(res.data.msg);
        })
        .catch((error) => {
          alert("DataSave failed!");
          console.log(error);
        });
    },
  },
  mounted() {
    console.log(this.dataAll[0][this.index].text);
    this.id = this.$route.params.id;
    this.browse();
  },
};
</script>

Css

<style scoped>
* {
  color: #555;
}
#main {
  text-align: center;
}

select {
  padding-left: 5px;
  width: 90px;
  border: #ddd solid 1px;
  border-radius: 5px;

  background: #eee;
  font-size: 14px;
}

.selected {
  background-color: #ddd;
}

.add {
  font-size: 14px;
  color: #444;
  margin-left: 10px;
  font-size: bold;
}
.add:hover {
  cursor: pointer;
}
textarea {
  color: #444;
  background: #eee;
  resize: none;
}

.text {
  float: left;
  margin: 30px 0px 30px 30px;
  padding: 10px;
  font-size: 16px;
  height: 140px;
  width: 90%;
  border: #ccc solid 1px;
  border-radius: 10px;

  float: left;
}
.four > div {
  float: left;
  margin-left: 29px;
  margin-bottom: 25px;
  border: #ccc solid 1px;
  height: 230px;
  width: 20.4%;
  border-radius: 10px;
}
.four > div > div > ul > li {
  list-style: none;
  cursor: pointer;
  font-size: 14px;
}
.four > div > div > ul > li:hover {
  background: #ddd;
}
.four > div > h1 {
  margin-top: 10px;
}

.ShiTi > li > p {
  color: #486e53;
  font-weight: bold;
  font-size: 14px;
  text-align: left;
  margin-left: 10px;
}
.ShiTi > li > p > span:first-child {
  font-size: 13px;
  font-weight: bold;
}
.ShiTi > li > p > span:nth-child(2) {
  font-size: 13px;
  font-weight: 400;
}

.GuanXi > li {
  border: #ddd solid 1px;
  height: 49px;
  width: 130px;
  margin-left: 10px;
  margin-bottom: 5px;
  border-radius: 3px;
}
.GuanXi > li > p {
  line-height: 18px;
  font-size: 13px;
}

.GuanXi > li > input {
  text-align: center;
  border: none;
  font-size: 13px;
  height: 30%;
  width: 100%;
  font-size: 14px;
  color: #363636;
  background: #eee;
}

.GuanXi > li > p:nth-child(2) {
  color: #486e53;
  font-weight: bold;
}

img {
  float: left;
  height: 40px;
  margin-left: 100px;
  margin-right: 60px;
}
button {
  cursor: pointer;
  float: left;
  border: none;
  background: rgb(107, 146, 77);
  color: white;
  padding: 8px 10px;
  font-weight: bold;
  border-radius: 15px;
  margin-left: 136px;
  width: 190px;
}
</style>

后端

Browse(database_id)

以下代码为Python函数Browse(database_id)的定义及解释说明,具体如下:

1. 函数功能描述

该函数的作用是从数据库中读取指定database_id下的所有DataFiles文件中的数据,并转换为Json格式。函数返回Json对象组成的列表,表示Database下全部DataFiles所包含的标注数据。

2. 函数参数说明

函数需要一个参数:database_id,为需要读取哪个数据库下的DataFiles数据。

3. 函数返回值

函数返回值是一个Json对象组成的列表,表示Database下全部DataFiles所包含的标注数据。

4. 函数实现逻辑

首先调用DataExport()函数获取对应database_id下的所有DataFiles文件的路径列表files_list,然后针对每一个DataFiles文件,读取其内容并将其转化为Json格式的列表,并添加至data_list中,最终返回该列表。

5. 其他说明

在函数中使用了其他两个函数:DataExport()和changeJsonFormat()。其中DataExport(database_id)函数用于获取指定database_id下的DataFiles文件列表的完整路径,而changeJsonFormat()则用于将读取的文件内容字符串转化为Json对象。

# 读取数据库下DataFiles的所有文件数据
def Browse(database_id):
    """
    作用:读取数据库下DataFiles的所有文件数据
    输入:database_id
    输出:数据库下DataFiles的所有文件数据
    """
    data_list = []
    data = ''
    files_list = DataExport(database_id)

    name = FindDatabase(database_id)
    for f_path in files_list:
        c = ''
        for f in open(f_path, 'r', encoding='utf-8'):
            c += f
        # print(c)
        data_list.append(changeJsonFormat(c))
    if len(data_list) == 0:
        return '数据为空'

    print('Browse', '成功')
    return data_list

# print(Browse(database_id = '1003172907'))

DataSave(dataInput, database_id)

以下代码为Python函数DataSave(dataInput, database_id)的定义及解释说明,具体如下:

1. 函数功能描述

该函数的作用是保存前端标注的数据。\n 这里假设数据一段一段地进行保存。函数先将前端传来的数据项按照段落(即一页)来存储,每次只保存一个段落的数据。函数先清空已有的数据文件,然后将新的数据写入到文件中,以字符串的形式写入每一行。

然后,函数会检查每个段落标注数据与数据集中已有的文本是否相似。如果有相似的文本,则更新该文本对应的“nodes”和“rels”数据,并将更新后的数据再次写入到数据文件中。

最后,函数会打印执行结果提示信息。

2. 函数参数说明

函数需要两个参数:dataInput和database_id,分别表示前端传来的标注数据和该数据所在的数据库ID。

3. 函数返回值

函数并没有返回任何值。

4. 函数实现逻辑

首先调用DataPath(database_id)函数获取对应database_id下的DataFiles文件路径列表files_list,接着遍历每个文件,使用"w"模式打开文件清空原内容,接着遍历每个段落数据,使用"a"模式打开文件并将段落数据按行写入文件中。

接着,函数针对每个段落数据,遍历files_list文件列表,读取文件内容,逐行解析为Json格式的对象,并将这些对象存放在一个列表data_list中。随后,函数会检查保存的数据是否与数据集的已有文本相似。如果文本相似,则更新该文本对应的节点("nodes")和关系("rels")信息,并将更新后的数据写入数据文件中。

最后,再次使用"a"模式打开文件并将更新后的数据按行写入文件中,以覆盖原有的数据。

5. 其他说明

函数中使用了其他三个函数:DataPath()、Eval()和Print()。其中DataPath(database_id)函数用于获取指定database_id下的DataFiles文件路径列表,而Eval()函数用于将读取的文件内容字符串解析为Json对象。Print()方法用于在控制台打印函数执行成功的提示信息。

# 保存前端标注的数据,每次保存一个段落(即一页)的数据
def DataSave(dataInput, database_id):
    """
    作用:保存前端标注的数据
    输入:前端传来的标注数据、数据集id
    输出:
    """
    files_list = DataPath(database_id)
    # 遍历数据集中的DataFiles,只有一个
    for f_path in files_list:
        # 清空原文件
        with open(f_path, 'w', encoding='utf-8') as f:
            f.write('')
        # 写入新内容
        for i in dataInput:
            with open(f_path, 'a', encoding='utf-8') as f:
                f.write(json.dumps(i, ensure_ascii=False))
                f.write('\n')
    print('DataSave', '成功')
    # 读取段落数据的key(“id”)和value
    for key, item in dataInput.items():
        # 遍历数据集中的DataFiles
        for f_path in files_list:
            data_list = []
            # 遍历file里的data(按行读取)
            for f in open(f_path, 'r', encoding='utf-8'):
                data_list.append(eval(f))
            for i in data_list:
                data = i
                for original_key, original_item in i.items():
                    # 匹配输入数据的文本与数据集中的文本,匹配相似率大于0.95,则用新数据覆盖旧文本中的"nodes"和"rels"数据
                    if item['oid'] == original_item['oid']:
                        data_list.remove(i)
                        data['id']['text'] = item['text']
                        data['id']['nodes'] = item['nodes']
                        data['id']['rels'] = item['rels']
                        data_list = [data] + data_list
            # 清空原文件
            with open(f_path, 'w', encoding='utf-8') as f:
                f.write('')
            # 写入新内容
            for i in data_list:
                with open(f_path, 'a', encoding='utf-8') as f:
                    f.write(json.dumps(i, ensure_ascii=False))
                    f.write('\n')

  • 3
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

人工智能技术小白修炼手册

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值