初探attention—attention原理和代码详解

attention

在正式开始探索attention之前,首先了解一下seq2seq。循环神经网络只能将一个序列信号转换为定长输出,但Seq2Seq可以实现一个序列信号转化成一个不定长的序列输出,因此seq2seq模型应用广泛,可以应用于很多不对等输入输出的场景,比如机器翻译,文本摘要,对话生成,诗词生成,代码补全等领域,当然也可以用于文本分类等任务。

以下所有内容,如有侵权,请联系删除~

seq2seq

Seq2Seq不特指具体方法,只要满足输入序列到输出序列的目的,都可以统称为Seq2Seq模型。如下图所示就是一个seq2seq模型。

seq2seq是sequence to sequence的缩写。前一个sequence称为编码器encoder,用于接收源序列。后一个sequence称为解码器decoder,用于输出预测的目标序列。

Seq2Seq模型的主要瓶颈是需要将源序列的全部内容压缩为固定大小的矢量。如果文本稍长,则很容易丢失文本的某些信息。

attention的提出

在seq2seq方法中,seq2seq中间语义向量最简单的方式是用encoder最后一层最后一个时间步的隐状态向量\(h_T\),作为中间语义向量C,或者通过激活函数变换到某个维度:

\(C=q(h_1,h_2,...,h_T)\)

这样,seq2seq无论输入长度和输出长度是多少,中间的语义编码C的长度是固定的,在文本长度较长时,会丢失很多信息。

另外,解码器从语义编码C到输出的过程,可以由下式表示:

\(Y_1=f(C)\\Y_2=f(C,Y_1)\\Y_3=f(C,Y_1,Y_2)\)

根据上式,显而易见,语义编码C这个向量的各个维度的信息对输出结果的影响力是相同的。

实际情况中,针对当前输出结果,语义编码C中的各个维度应该表现出不同的影响力。比如i have a dog,在翻译小狗的时候,dog这个词向量的影响力应该大于其他单词向量的影响力。基于这种思想,attention被提出来。

Attention,正如其名,针对语义编码C增加注意力机制。首先利用编码器得到encoder中的hidden state,编码器可以是RNN,LSTM及其变种,或者GRU。该模型在decode阶段,会在中间语义向量中选择最适合当前节点的上下文向量作为输入。

attention与传统的seq2seq的区别

encoder可以提供更多的数据给decoder,encoder会把所有的节点的hidden state提供给decoder,而不仅仅只是encoder最后一个节点的hidden state。

decoder并不是直接把所有encoder提供的hidden state全部作为输入,而是采取一种权重赋予机制,给不同的hidden state赋予不同的选择权重。

以上两点是attention与传统seq2seq的主要区别。权重的获取方式代表不同的attention模型。最简单最常用的是dot product乘积矩阵,即将解码器的输出隐状态与中间语义编码进行矩阵相乘获得权重。

attention详细步骤拆解

附一张通俗易懂的图:

attention处理步骤主要有以下几步:

  1. 把encoder每一个节点的hidden states的值与decoder当前节点的上一个节点的隐状态\(h_{t-1}\)相乘(如果是第一个decoder节点,需要初始化一个hidden state)。

这样会获得time_steps个值,这些值就是语义编码C每个hidden state的分数,也就是权重分数。在不同的decoder时间步,由于decoder上一个节点的\(h_{t-1}\)是不断变化的,因而这个权重向量也是不同的;

  1. 归一化。把 2. 中的分数值进行softmax计算,计算之后的值就是每一个encoder节点的hidden states对于当前decoder时间步的权重。

  2. 把权重与语义编码C中的hidden states相乘并相加,得到的结果即是当前decoder时间步的上下文向
    量。

  3. 将上下文向量和decoder的上一时刻的隐层状态输入decoder网络,得到当前时刻的输出。

  4. decoder开始下一节点,并重复1-5;直到输出所有的结果。

抛开encoder-decoder,Attention机制其实就是注意力系数分配的过程。常见各种技术文章谈到的K,V,Q,看起来玄乎其玄,其实万变不离其宗,其原理就是上述的权重分配的过程。

可以将中间语义向量C想象成一个<K,V>对,也就是键值对。而Q是输出的某个元素。我们首先利用Q计算与K的相关性,得到key对应value的权重,然后利用权重对value加权求和,得到最终的attention值。这个过程可以分解为三个阶段:

  • Q与K进行相似度计算,获得权值
  • 对权值归一化
  • 利用归一化的权值与value进行加权求和,得到输出值。

上述中K与V其实是相同的,不同的是Q

attention实现及讲解

首先看代码,为了对代码有一个清晰的了解,只保留了部分代码。源代码仓库:点击这里

import os
from tensorflow.keras import backend as K
from tensorflow.keras.layers import Dense, Lambda, Dot, Activation, Concatenate, Layer


class Attention(object if debug_flag else Layer):

    def __init__(self, units=128, **kwargs):
        super(Attention, self).__init__(**kwargs)
        self.units = units

    def build(self, input_shape):
        input_dim = int(input_shape[-1])
        with K.name_scope(self.name if not debug_flag else 'attention'):
            self.attention_score_vec = Dense(input_dim, use_bias=False, name='attention_score_vec')
            self.h_t = Lambda(lambda x: x[:, -1, :], output_shape=(input_dim,), name='last_hidden_state')
            self.attention_score = Dot(axes=[1, 2], name='attention_score')
            self.attention_weight = Activation('softmax', name='attention_weight')
            self.context_vector = Dot(axes=[1, 1], name='context_vector')
            self.attention_output = Concatenate(name='attention_output')
            self.attention_vector = Dense(self.units, use_bias=False, activation='tanh', name='attention_vector')
    # noinspection PyUnusedLocal
    def call(self, inputs, training=None, **kwargs):
        """
        Many-to-one attention mechanism for Keras.
        @param inputs: 3D tensor with shape (batch_size, time_steps, input_dim).
        @param training: not used in this layer.
        @return: 2D tensor with shape (batch_size, units)
        @author: felixhao28, philipperemy.
        """
        if debug_flag:
            self.build(inputs.shape)
        # 第一步
        score_first_part = self.attention_score_vec(inputs)  
        # 第二步
        h_t = self.h_t(inputs) 
        # 第三步
        score = self.attention_score([h_t, score_first_part])
        # 第四步
        attention_weights = self.attention_weight(score) 
        # 第五步
        context_vector = self.context_vector([inputs, attention_weights])
        # 第六步
        pre_activation = self.attention_output([context_vector, h_t])
        attention_vector = self.attention_vector(pre_activation)

        return attention_vector

这个版本的attention的计算步骤为:

第一步:将隐状态序列进行一次线性变换,隐状态的shape为:(batch_size, time_steps, hidden_size),权重W的维度为(hidden_size, hidden_size),二者相乘得到的shape为:(batch_size, time_steps, hidden_size)。为什么要加线性变换?

第二步:获取decoder的初始节点的上一时刻隐状态变量\(h_{t-1}\),此处直接使用encoder隐状态序列的最后一个隐状态\(h_t^{encoder}\)

第三步:获取当前节点的权重分数,通过使\(h_{t-1}^{decoder}\) dot score_first_part。经过线性变换的隐状态序列的shape为:(batch_size, time_steps, hidden_size) ,\(h_{t-1}^{decoder}\)的shape为:(batch_size, hidden_size),输出结果的shape为:(batch_size, time_steps)。

第四步:利用softmax归一化权重分数

第五步:利用权重和输入的隐状态序列获得attention的当前节点输入。encoder输出的隐状态序列shape为:(batch_size, time_steps, hidden_size),权重shape为:(batch_size, time_steps),二者相乘获得的结果shape为: (batch_size, hidden_size),此结果作为输入送进decoder网络,假设此结果变量名为context_vector。

第六步:利用\(h_{t-1}\)和context_vector,推理出当前时刻的输出,最后经过全连接层获得此节点的最终输出。

使用attention

那么如何使用attention呢,作者也给出了详细的例程:

import numpy as np
from tensorflow.keras import Input
from tensorflow.keras.layers import Dense, LSTM
from tensorflow.keras.models import load_model, Model

from attention import Attention

def fun():
    model_input = Input(shape=(time_steps, input_dim))
    x = LSTM(64, return_sequences=True)(model_input)
    x = Attention(units=32)(x) # 这里用到attention
    x = Dense(1)(x)
    model = Model(model_input, x)
    model.compile(loss='mae', optimizer='adam')
    model.summary()

attention暂时到这~

参考:

http://imzhanghao.com/2021/09/01/attention-mechanism/

http://arxiv.org/abs/1409.0473

http://arxiv.org/abs/1508.04025

本文转载于网络 如有侵权请联系删除

相关文章

  • 面试必问的线程池,看这一篇就够了

    前言 学习完了阻塞队列之后,接下来,我们学习下面试必考的知识点--线程池。用过JAVA的同学一定听过线程池的大名。下面我们就来看看这个大名鼎鼎的家伙。为啥要用线程池呢?第一个问题来了,为啥要使用线程池呢?直接new一个线程它不香么?就像这样newThread(newRunnable(){ publicvoidrun(){ longTest.countTest(); } }).start() 复制简单又快捷,其中run方法的作用是用来执行一个任务单元(也就是一段代码)。start方法的作用是用来创建一个新线程,同时设置好这个线程的上下文,比如这个线程的栈,线程的状态等一系列的信息。 这些信息处理好之后这个线程才可以被调度,一旦调度,就会执行run()方法。 但是在实际项目中是禁止这样做了,在阿里出的JAVA开发手册中就明确说了原因:所以,直接new一个线程不香,原因主要在于创建大量相同的线程会大量的消耗系统内存,甚至会导致系统内存耗尽;同时,大量的线程会竞争CPU的调度,导致CPU过度切换。 在这里插入图片描述 接下来我们就看看香香的线程池的优点。线程池的优点减少在创建和销毁线程上所花费

  • 安装多个jdk导致eclipse打不开问题

    问题描述本来使用的是jdk1.8,由于其他原因需要使用jdk1.6,在安装完jdk1.6后打开eclipse就会报错: Version1.6.0_43oftheJVMisnotsuitableforthisproduct.Version:1.7orgreaterisrequired.问题原因:jdk1.6安装完成时,会将java.exe、javaw.exe、javaws.exe放入环境变量Path的对应的某个目录中(win10是在C:\Windows\system32下)。由于Path值顺序的原因,在执行某个命令时(如:java-version),会按顺序搜索目录,如果已经搜索到命令则不会再继续往后搜索。解决方法方法1:C盘搜索java.exe,找到对应目录,删除java.exe、javaw.exe、javaws.exe即可(推荐) 方法2:调整Path变量值的顺序,点击上移,调整到靠前位置:

  • MySQL必知必会的sql语句

    如何查看有什么数据库?showdatabases;复制如何选择数据库?usedatabases_name eg: usetest;复制如何查看该数据库中有哪些表?showtables;复制如何查询表中的数据?select*fromtableName;复制如何退出数据库服务器?exit;复制如何在数据库服务器中创建自己的数据库?createdatabasedatabaseName;复制如何创建一个数据表?创建一个pet表createtablepet( idint(20), namevarchar(20), ageint(5), brithdate );复制如何查看数据表的架构?desctableName;复制如何插入数据?INSERTINTOpetVALUES(1,'cc',18,'1998-8-2');复制插入指定字段的数据?INSERTINTOdemo(id)VALUES(18),(13);复制删除数据DELETEFROMtablesNameWHRER条件;复制修改数据UPDATEtableNameSET字段1=值1,字段2=值2...WHER

  • 12-初识OpenStack中的网络

    引入现在有这样一个场景,有两组比赛的选手,一个是云计算队,一个是网络安全队它们的比赛都要在云平台上进行操作,现在为它们各自划分不同的两个网络创建网络我们直接从创建网络这张图来了解Neutron创建网络注意是在管理员选项下的网络管理员选项名称创建的这个网络叫什么名字,区分识别操作它的一个标识项目这个网络属于哪个项目,这里我们有云计算和网络安全两个比赛项目,所以待会我们去创建这两个项目网络类型点开下拉框可以看到有,Local,Flat,VLAN,GRE,VXLAN,Geneve这几种网络类型网络类型在网络节点中(这里控制节点和网络节点都是同一个),neutron中有一个ml2_conf.ini,定义了它的网络类型[root@controller/]#cat/etc/neutron/plugins/ml2/ml2_conf.ini [ml2] #当前网络类型驱动 type_drivers=flat,vlan,vxlan,gre,local #当前租户(项目)使用的网络 tenant_network_types=vxlan #交换机类型是openvswitch mechanism_driv

  • 有限状态机模型

    在阅读harbor源码时,在jobservice代码中,发现实现了一个有限状态机。状态管理在系统设计中常被使用。通过实现它,可以方便的对程序的状态进行管理。状态在现实生活中,有很多存在的例子。例如,灯有开,关两种状态,当然如果较真的话,中间还可以有多个亮度的状态。红绿灯,登录状态,程序的生命周期等等,这个太多了。状态机有限状态机(英语:finite-statemachine,缩写:FSM)又称有限状态自动机,简称状态机,是表示有限个状态以及在这些状态之间的转移和动作等行为的数学模型。既然是一个数学模型,那么就可以按照数学的方式对其下定义。不是搞学术的,也就没必要对其深究,点到为止。但是有两点需要特此说明,1.状态集合是一个有限集合2.状态是离散的存在对状态机进行抽象,可以归纳出以下属性:1.当前的状态2.事件3.行为 //有限状态机数据结构 typeFSMstruct{ musync.Mutex//排他锁 statestring//当前状态 handlersmap[string]map[string//处理handle }复制以上,我们实现了一个状态机的数据结构,接下来,我们来实现这个

  • 阿里AI解锁车场景:达摩院技术输出,天猫精灵进驻奔驰、奥迪和沃尔沃

    李根发自大山子 量子位报道|公众号QbitAI阿里AI今日又解锁了新场景,首次进入车空间。 首批合作伙伴也来头不小:奔驰、奥迪和沃尔沃。今年6月开始,奔驰、沃尔沃相关的车中,就会集成阿里AI推出“家车-空间融合”的解决方案。阿里AI实验室负责人浅雪说:汽车经过百年发展,是时候迈入AI时代。而阿里方面试图利用达摩院技术,通过天猫精灵能力输出,把汽车打造成家庭的第二空间。具体Howtodo?阿里“AI+车”5项能力主要有5大方面的能力。分别是:家-车空间融合,人车自然交互,云车内容一体,随行生活助手,智能语音工具。我们一一看下怎么做。家-车空间融合,主要是打通家、车,并实现数据互通。阿里AI方面设想的场景是:车主在家中,就能远程查看和控制车,查看引擎、车胎、油量等;车主在车里,也能查询家庭场景的状态,可以远程控制家及让家居进入联动自动模式——比如车进小区,空调自动打开。最关键的是数据同步,可以在车上接着听同一首歌,以及议程、日程等多终端多场景同步。人车自然交互,能力则来自令人熟悉的“天猫精灵”。可以简单理解为车内新增了一台阿里AI音箱,可以语音交互对话,不同的是,这项自然交互里还有“视觉”

  • mongodb用mongoose查库的对象,不能增加属性修改属性

    node+koa2+mongodb写了一个给前端的接口 如果不是写这个接口,这辈子都发现不了mongodb里这个大坑 mongoose是个ODM(ObjectDocumentMapper),mongodb是nosql数据库,文档存储mysql,sqlserver,oracle都是关系型数据库 所以mongodb无法在取到对象增加属性,必须在追加时候重新用一个对象,或者在schema中添加这个对象的key model.js //这里用来建数据库表结构相关的 constmongoose=require('mongoose'); mongoose.connect('mongodb://127.0.0.1:27017/zhongtong',{ useNewUrlParser:true });//连接数据库 letcategorySchema=newmongoose.Schema({ name:String }), categoryModel=mongoose.model('category',categorySchema),//分类 contentSchema=newmongo

  • 39. 组合总和

    给定一个无重复元素的数组 candidates 和一个目标数 target ,找出 candidates 中所有可以使数字和为 target 的组合。 candidates 中的数字可以无限制重复被选取。 说明: 所有数字(包括 target)都是正整数。解集不能包含重复的组合。 示例 1: 输入:candidates=[2,3,6,7],target=7,所求解集为:[[7],[2,2,3]]示例 2: 输入:candidates=[2,3,5],target=8,所求解集为:[ [2,2,2,2], [2,3,3], [3,5]]  提示: 1<=candidates.length<=301<=candidates[i]<=200candidate中的每个元素都是独一无二的。1<=target<=500   #include<iostream> #incl

  • prometheus-altermanager告警

                                     

  • API的文件遍历,未使用CFileFind,因为里面牵扯MFC,编个DLL好麻烦。

    1//FindFileDebug.cpp:定义控制台应用程序的入口点。 2// 3 4#include"stdafx.h" 5#include"FindFileDebug.h" 6 7#ifdef_DEBUG 8#definenewDEBUG_NEW 9#endif 10 11#defineIS_DIRECTORY(x)((x)&(FILE_ATTRIBUTE_DIRECTORY)) 12#defineIS_FAILED(0) 13 14voidTraversFile(CStringcsPath,CString&csDatabuffer); 15BOOLIsDot(LPCTSTRptStr); 16usingnamespacestd; 17 18#defineTESTPATH("G:\\Python脚本\\PyCahrm项目") 19 20intmain() 21{ 22intnRetCode=0; 23CStringfilepath=CString(TESTPATH); 24CStringFileBuffer; 25TraversFile(filepath,FileB

  • 前端知识学习03

    1.null和undefinded区别: null表示一个对象被定义的,值为"空值" 作为函数的参数,表示该函数的参数不是对象 作为对象原型链的终点 undefined 如果变量被声明,但是没有赋值,就等于undefined 调用函数时,应该提供的参数没有提供,该参数等于undefined 对象没有赋值的属性,该属性的值为undefined 函数没有返回值时,默认返回undefined 2.判断数组和对象 Array.isArray([]) []instanceofArray Object.propertype.toString.call([]) [].constructor 3.跨域问题 jsonp:只限于get请求 设置请求头:Object.stHeader('AccessAllowContralOrigin','*') Proxy代理 4.重绘与回流 重绘(repaint):渲染树节点发生改变,但是不影响节点在页面中的空间位置及大小。例如div节点中的背景颜色,字体颜色发生改变,会引起重绘。 回流(reflow):也叫做重排,当渲染树节点的变化引起节点位置及大

  • Pycharm远程连接服务器(windows下远程修改服务器代码)

      1、写在前面   之前一致用putty,ssh,修改代码,或者本地修改,上传到服务器,各种不爽,现在改用xshell,但是有时候还是不方便感觉,于是自己配置了远程连接pycharm,这样不用总是到代码里修改,直接在windows下pycharm里修改再保存就可以实现同步更新到服务器里的代码里了。 2、content    打开pycham,windows下连接服务器端如图所示: 配置连接服务器,name随便写,connection下,协议sftp,服务器主机IP,用户名,密码,apply   点击TestSFTPconnection会发现,如果连接成功会提示你如下:     下面选择连接windows下的那部分代码和服务器上代码相连,Mappings,本地Localpath,服务器path,apply,OK,表示已经把本地的代码和服务器代码连接上了。     设置如何使得本地代码和服务器代码同步更新,如图,       如下图,选

  • web测试实践(2)

    今天进行阶段一:a基本功能分析 阶段一:b前端性能分析  

  • 电脑基本知识收藏

    一、U盘分区四种系统优缺点对比:  U盘分区格式的好坏,直接会影响到文件传输的性能、速度等等。因此大家在对U盘进行分区的时候,应该慎重的选择使用什么样的u盘格式。但是大家都不知道哪一种u盘格式好,哪一种不好,下面我们就来重点介绍4种最常见的U盘格式:FAT16、FAT32、NTFS、ExFAT,而且这4种u盘格式,每一种的优缺点都不一样。 FAT16格式: 优点:兼容性最好,某些数码设备可能对FAT32和NTFS格式的存储卡支持不太好,因此只能使用FAT16。 缺点:最大仅支持2GB分区,空间浪费大。 备注:赶紧看看您的U盘是不是FAT格式,如果是就改用FAT32吧,FAT32是FAT16的升级版。 FAT32格式: 优点:兼容性好。 缺点:单个文件不能超过4GB,不支持512MB以下容量的U盘。 备注:如果U盘容量达8GB以上,发现4GB文件拷不进去的话,可以考虑换用NTFS或ExFAT格式了。 NTFS格式: 优点:兼容性好,支持任意大小的U盘。 缺点:会缩短闪存

  • 关于编码

    什么是编码 计算机是处理二进制的机器,自出现一来人们一直在优化着人与机器间的交互方式。(→_→)一切都是因为二进制的01010看起来太反人类了。 密密麻麻的出现时,简直是灾难。所以汇编语言出现了,大家不用二进制的方式编码了,写出来的东西也没那么难看懂了。再后来更高级的语言,FORTRAN,C 出现了。本质上无论是何种语言编写的程序,最后都会以二进制形式运行于内存之中。所以,从广义上来说编程语言是二进制机器语言的一个“编码集”。 (:з)∠)可以用同样的方式来理解文本编码,所谓的编码,本质上是定义二进制语言的字典。据我所知,最早的编码集是1963年publish的ASCII,他主要处理的问题是,定义一个二进制映射到字符的表。比如说二进制的1000001表示字符A。然而ACSII编码只用了一个bytes的空间,一bytes有8个bits,最多只能表示256个字符,自然没法囊括文明世界的全部语言。后来,就出现了各种编码集来兼容不同的语言。 Unicode 这些编码集的出现没有完全解决问题,反而使问题变得更复杂。因为他们互不兼容,没法统一。这个时候Unicode就出现了(及UCS)。 Unic

  • Date 和 LocalDate

    1, Date转LocalDate:newDate().toInstant().atZone(ZoneId.systemDefault()).toLocalDate()

  • PHP排序算法

    一、快速排序 先用一个基准元素,将数组分成两部分,一部分比基准元素小,一部分大于等于基准元素。此时基准元素在其排好序后的正确位置,然后再用同样的方法递归地排序划分的两部分。 functionquickSort($arr){ //先判断是否需要继续进行 $length=count($arr); if($length<=1){ return$arr; } //选择第一个元素作为基准 $base_num=$arr[0]; //遍历除了标尺外的所有元素,按照大小关系放入两个数组内 //初始化两个数组 $left_array=array();//小于基准的 $right_array=array();//大于基准的 for($i=1;$i<$length;$i++){ if($base_num>$arr[$i]){ //放入左边数组 $left_array[]=$arr[$i]; }else{ //放入右边 $right_array[]=$arr[$i]; } } //再分别对左边和右边的数组进行相同的排序处理方式递归调用这个函数 $left_array=quickSort($

  • React-Native 之 TextInput使用

    前言 学习本系列内容需要具备一定HTML开发基础,没有基础的朋友可以先转至HTML快速入门(一)学习 本人接触ReactNative时间并不是特别长,所以对其中的内容和性质了解可能会有所偏差,在学习中如果有错会及时修改内容,也欢迎万能的朋友们批评指出,谢谢 文章第一版出自简书,如果出现图片或页面显示问题,烦请转至简书查看也希望喜欢的朋友可以点赞,谢谢 TextInput文本输入框 ReactNative中的文本输入框使用和iOS比较相近,可能是因为RN首先封装iOS端的缘故(这点对iOS开发者来说是个好消息) TextInput也是继承自View,所以View的属性TextInput也能使用,一些样式类的属性可以参照View的相关属性 为了更好的讲解TextInput,先创建一个基本的文本输入框 //视图 vartextInputTest=React.createClass({ render(){ return( <Viewstyle={styles.container}> {/*文本输入框*/} <TextInputst

  • 建立索引的原则

    1.最左前缀匹配原则,非常重要的原则,mysql会一直向右匹配直到遇到范围查询(>、<、between、like)就停止匹配,比如a=1andb=2andc>3andd=4如果建立(a,b,c,d)顺序的索引,d是用不到索引的,如果建立(a,b,d,c)的索引则都可以用到,a,b,d的顺序可以任意调整。 2.=和in可以乱序,比如a=1andb=2andc=3建立(a,b,c)索引可以任意顺序,mysql的查询优化器会帮你优化成索引可以识别的形式。 3.尽量选择区分度高的列作为索引,区分度的公式是count(distinctcol)/count(*),表示字段不重复的比例,比例越大我们扫描的记录数越少,唯一键的区分度是1,而一些状态、性别字段可能在大数据面前区分度就是0,那可能有人会问,这个比例有什么经验值吗?使用场景不同,这个值也很难确定,一般需要join的字段我们都要求是0.1以上,即平均1条扫描10条记录。 4.索引列不能参与计算,保持列“干净”,比如from_unixtime(create_time)=’2014-05-29’就不能使用到索引,原因很简单,b+

  • PID算法学习记录

    最近做项目需要用到PID算法,这个本来是我的专业(控制理论与控制工程),可是我好像是把这个东西全部还给老师了。 没办法,只好抽时间来学习了。 先占个座,后续将持续更新! 龙心文

  • QtnProperty代码编译

    代码地址 GitHub:QtnProperty 编译环境 IDE:MicrosoftVisualStudioCommunity2019,16.11.15 Qt:5.12.5_msvc2017,32bit OS:Windows10家庭版,21H2 QtVisualStudioTools:2.8.1.6 复制 修改综述 修改项目文件 VS导入pro文件时,QtnPropertyDemo和QtnPropertyTests这两个项目无法加载,手动重新加载分别出现以下错误: F:\Code\Library\QtnProperty\Demo\QtnPropertyDemo.vcxproj:error:无法加载具有重复项目项的项目:Demo.pef作为CustomBuild且作为CustomBuild项类型包括在其中。 复制 F:\Code\Library\QtnProperty\Tests\QtnPropertyTests.vcxproj:error:无法加载具有重复项目项的项目:PEG\test.pef作为CustomBuild且作为CustomBuild项类型包括在其中。 复制 查看Qtn

相关推荐

推荐阅读