量化投资交易 vn.py

前言:

当初接触到vnpy,一开始当然是按照该项目在GitHub上的指南,开始安装,配置,阅读Wiki,但是作为一个python新手,并不能马上利用vnpy来写策略回测甚至实盘。所以我决定还是从源码看起,一点一点摸透整个框架的细节。虽然看源代码对于一个python初学者真的很困难,特别是期间得了干眼症,看显示器那叫一个难受,但还是坚持下来。


看了一遍之后,把自己对vnpy的一些理解发上来,一来,希望和大家多交流,毕竟自己编程方面不是高手,肯定有理解的不对的地方,希望大家指正,二来再阅读一次代码,看看之前有没有遗漏疏忽的地方,另外,我确实认为vnpy是一个非常好的项目,非常适合学习和使用,但很多初学量化的人都像我一样并不是计算机科班出身,写一篇详细的使用指南可以帮助初学者节约时间,并更好的使用vnpy。


需要强调的是,整篇文章还在持续更新,会根据需要修改文章,特别是希望能与大家多交流,不管是任何问题,如有指点,希望不吝赐教。


当然还要感谢

@用python的交易员
,vnpy真是太棒了!


废话不多说,Let's beginning!


从回测开始说起:

对于这么复杂的系统,从什么地方开始是一个问题,一开始比较心急,按照文件的顺序一个一个读,想一次性消化整个系统,后来发现效率很低,代码连不到一起,所以读了几个就放弃了。转而换了一个思路,在\examples\CtaBacktesting文件夹下有回测引擎的具体示例文件,分别是loadCsv.py,runBacktesting.py和runOptimization.py,就从这三个文件一步一步来看vnpy是如何进行回测的。

图示可以清楚看清loadCsv.py文件导入了哪些模块(忽略系统模块和一些第三方模块)

<img src="https://pic4.zhimg.com/v2-c318403bd3931057bf959af0b2185dd7_b.jpg" data-caption="" data-size="normal" data-rawwidth="1763" data-rawheight="564" class="origin_image zh-lightbox-thumb" width="1763" data-original="https://pic4.zhimg.com/v2-c318403bd3931057bf959af0b2185dd7_r.jpg"/>

我们来一个一个看


vtFunction.py

这里面包括了5个开发中常用的函数,

safeUnicode()

todayDate()

loadIconPath()

getTempPath()

getJsonPath()

其中vtGlobal.py导入的是getJsonPath()方法,作用是获取JSON配置文件的路径,就vtGlobal.py而言,它获取的是VT_setting.json的路径,一般你可以在\vnpy\trader找到,打开VT_setting.json,可以看到里面包含了一些设置,后面会用到。



vtGlobal.py

该文件就是将VT_setting.json里面的配置变成python可读取和使用的字典形式,并赋值给globalSetting,将它作为全局配置的字典。



__init__.py,constant.py,text.py

\vnpy\trader\language文件夹中有两个文件夹chinese和english,以及__init__.py文件,__init__.py默认设置为chinese,假如你想使用english,就可以在VT_setting.json里面修改。constant.py包含了近百个常量定义,仔细看可以把它们都归类成交易相关的常量,后面会经常用到。而text.py也定义了很多常量,可以把这些归类为显示相关的常量。



vtConstant.py

从constant.py导入了常量,并把它们添加到vtConstant.py的局部字典中。



ctaBase.py

定义了很多常量以及一个StopOrder类,定义的常量里面就包含了loadCsv.py里面导入的MINUTE_DB_NAME = 'VnTrader_1Min_Db',后面在数据库导入数据的时候会碰到。StopOrder类定义了一个本地停止单。



vndatayes.py

里面定义了一个DatayesApi类,用于从通联数据下载数据。



vtObject.py

定义了几种数据类,后面会经常用到。



ctaHistoryData.py

定义了CTA模块用的历史数据引擎,从里面定义的方法可以看出,主要是下载历史数据和将csv文件导入数据库

__init__()方法用到了vtGlobal.py里面导入的globalSetting,默认是localhost,创建了本地的数据库链接。另外一个就是通联数据下载的api,需要传入token参数。

暂且只关注loadMcCsv()方法,需要传入三个参数,filename就是历史数据文件名,dbName与ctaBase.py里面定义的常量有关,以IF0000_1min.csv为例,里面保存的是1分钟bar数据,就传入MINUTE_DB_NAME,同理tick数据就传入TICK_DB_NAME,日线数据就传入DAILY_DB_NAME。symbol就是标的的代码,例如IF0000。中间的代码按照csv文件保存数据的格式,把数据存入数据库。



loadCsv.py

所以整个代码完成的就是将csv历史数据的导入数据库。



图示可以清楚看清runBacktesting.py文件导入了哪些模块(忽略系统模块和一些第三方模块)

<img src="https://pic1.zhimg.com/v2-92d15cddd874f0aa5ab95c7717b5f560_b.jpg" data-caption="" data-size="normal" data-rawwidth="1817" data-rawheight="771" class="origin_image zh-lightbox-thumb" width="1817" data-original="https://pic1.zhimg.com/v2-92d15cddd874f0aa5ab95c7717b5f560_r.jpg"/>

eventEngine.py

里面定义了三个类,EventEngine,EventEngine2,Event,以及一个测试函数。EventEngine,EventEngine2两个类的代码内容差不多,我们只看EventEngine

EventEngine定义了事件驱动引擎,理解这个引擎是理解vnpy工作原理的重要一步。关于导入的Queue模块和threading模块,可以百度一下它们的用法


__init__()方法:

self.__queue = Queue() 实例化事件队列

self.__active = False 事件引擎开关,默认为False

self.__thread = Thread(target = self.__run) 创建Thread类的实例,传给它一个函数,当线程启动,该函数运行

self.__timer = QTimer() 计时器,用于触发计时器事件

self.__timer.timeout.connect(self.__onTimer) 将timeout信号和self.__onTimer方法绑定,当触发timeout信号,self.__onTimer方法运行

self.__handlers = defaultdict(list) 这里的__handlers是一个字典,用来保存对应的事件调用关系其中每个键对应的值是一个列表,列表中保存了对该事件进行监听的函数功能

self.__generalHandlers __generalHandlers是一个列表,用来保存通用回调函数(所有事件均调用)

下面是类中定义的方法,我们不以定义的顺序来看,而是按照事件的传递顺序来看。

start(self, timer=True):

引擎启动,timer表示是否要启动计时器,默认为True。

self.__active = True 将引擎设为启动

self.__thread.start() 启动事件处理线程

self.__timer.start(1000) 启动计时器,计时器事件间隔默认设定为1秒,start()时间参数的单位是毫秒,意思是1000毫秒后触发timeout,而timeout与self.__onTimer绑定,故self.__onTimer被调用

当start()方法执行,事件处理线程和计时器同时启动

事件处理线程self.__thread启动,而self.__thread = Thread(target = self.__run),也就是说,__run()方法执行


__run(self):

self.__active在start()方法中已经设置为True

event = self.__queue.get(block = True, timeout = 1) 获取事件的阻塞时间设为1秒,调用队列对象的get()方法从队头删除并返回一个项目。可选参数为block,默认为True。如果队列为空且block为True,get()就使调用线程暂停,直至有项目可用。如果队列为空且block为False,队列将引发Empty异常。

self.__process(event) 假设队列里面有项目,则执行__process()方法


__process(self, event):

事件处理方法,优先检查是否存在对该事件进行监听的处理函数,然后调用通用处理函数进行处理

计时器启动,self.__timer.timeout.connect(self.__onTimer)触发执行self.__onTimer


__onTimer(self):

创建计时器事件,调用put()方法向队列中存入计时器事件


put(self, event):

self.__queue.put(event) 调用队列对象的put()方法在队尾插入一个项目。


从上面可以看出,整个事件传递的过程是这样的:

调用start()方法,事件处理线程和计时器同时启动,计时器每隔一秒调用__onTimer()方法,创建计时器事件,调用put()方法在队尾插入一个事件,事件处理线程每隔一秒获取事件,若存在事件调用__process()方法,对事件进行处理。


stop(self):

停止引擎,事件处理线程和计时器


剩下的四个方法用于注册注销事件和通用事件处理函数监听

可以用test()函数自己验证一下




eventType.py

本文件仅用于存放对于事件类型常量的定义




vtEvent.py

基于vnpy.event.eventType,并添加更多字段




vtGateway.py

定义了VtGateway类作为交易接口,类方法都是关于事件的推送。

注意到一个细节,以onTick(self, tick)为例,参数tick是不是传入的是类VtTickData的实例,因为后面ctaBacktesting.py里面from vnpy.trader.vtGateway import VtOrderData, VtTradeData,而不是fromvtObject.py import VtOrderData, VtTradeData




ctaTemplate.py

写策略至关重要的部分,里面包含4个类,CtaTemplate,TargetPosTemplate,BarManager,ArrayManager,后面的例子没有用到TargetPosTemplate,我们暂时只看其他三个类


CtaTemplate

CTA策略模板,开发策略时需要继承CtaTemplate类

__init__():

初始化使用的ctaEngine,比如用回测引擎,可以在回测引擎类方法initStrategy中,有self.strategy = strategyClass(self, setting),传入的self参数代表BacktestingEngine(原来还可以这么传参数,学到了)

setting是设置策略的参数,示例是空字典。

由于CtaTemplate是用来继承,方法的具体应用将在后面用具体的策略说明。


BarManager

K线合成器

updateTick(self, tick):

用于将tick数据合成1分钟bar。

updateBar

用于将1分钟bar数据合成x分钟bar。


ArrayManager

K线序列管理工具,负责:1. K线时间序列的维护 2. 常用技术指标的计算




strategyKingKeltner.py

以具体的策略为例,看看如何使用上面的模板

首先设置策略的参数和变量,并把它们添加进列表

__init__():

类KkStrategy是继承自CtaTemplate的子类,所以初始化先调用CtaTemplate的__init__()方法。

按照策略是否需要,创建BarManager和ArrayManager的实例(因为我看到有的策略并没有调用BarManager和ArrayManager的类方法,而是根据策略自己写了另外的k线处理方法)

以KkStrategy为例:

self.bm = BarManager(self.onBar, 5, self.onFiveBar),从传入额参数可以看出这是一个基于5分钟k线的策略,第一个参数是1分钟k线回调函数,最后一个是5分钟回调函数。


onInit():

初始化策略

writeCtaLog是继承自CtaTemplate的方法,在CtaTemplate中能看到,该方法再次调用ctaEngine的writeCtaLog方法,用于记录日志。

initDays代表初始化需要的天数,本例中为十天,那么initData就是保存的十天的1分钟k线数据。然后调用onBar方法处理1分钟k线数据

回测中可以忽略putEvent()方法


onBar():

调用updateBar()方法,如果策略用的是1分钟k线数据,那么这个函数就是用于实现整个策略的主体部分。

从updateBar()方法可以看出,首先更新推送进来的数据,合成5分钟k线,若当前时间能否被5整除,则调用onXminBar方法,本例就是onFiveBar


onFiveBar():

本例策略的思想就在这里实现。首先要撤销之前发出的尚未成交的委托,再来就是保证指标可以计算,当inited为True时,表示当Array里面缓存的数据长度大于等于规定的size,也就是说可以计算相关指标了。然后计算指标数值。下面的代码都是开仓平仓的条件判断,就不详细说明了。


sendOcoOrder():

自定义的委托函数,用于突破时入场


onTrade():

用于成交后撤销委托


onStop():

停止策略


onTick():

处理tick数据,本例中没有用到,所以不调用



ctaBacktesting.py


里面定义了四个类BacktestingEngine,TradingResult,DailyResult,OptimizationSetting


BacktestingEngine

定义了回测引擎类,使用的策略代码和实盘一样


__init__(self):

需要设置回测的初始化参数都在里面,一般来说需要设置的有

self.strategy = None 回测的策略

self.mode = self.BAR_MODE 回测的模式,默认为bar

self.startDate = '' 回测起始时间,默认为空

self.initDays = 0 回测需要初始化的数据天数,即前面用于预先载入的历史数据的天数

self.endDate = '' 回测结束时间,默认为空

self.capital = 1000000 初始化本金,默认为100W

self.slippage = 0 回测的滑点,默认为0

self.rate = 0 回测的佣金比率,默认为0

self.size = 1 合约大小,默认为1

self.priceTick = 0 价格最小变动,默认为0

self.dbName = '' 回测的数据库名

self.symbol = '' 回测的标的名

self.dataStartDate = None 格式化后的回测起始时间

self.dataEndDate = None 格式化后的回测结束时间

self.strategyStartDate = None 策略开始时间,即回测开始时间加上初始化数据的天数

(跳过通用功能)

根据需要,调用下面的类方法设置参数


setStartDate(self, startDate='20100416', initDays=10):

用于设置策略的开始时间。


setEndDate(self, endDate=''):

用于设置策略的结束时间。


setBacktestingMode(self, mode):

设置回测模式,有tick和bar可选


setDatabase(self, dbName, symbol):

设置用到额数据库以及标的名称


setCapital(self, capital):

设置本金


setSlippage(self, slippage):

设置滑点


setSize(self, size):

设置合约大小


setRate(self, rate):

设置佣金比率


setPriceTick(self, priceTick):

设置最小价格变动


initStrategy(self, strategyClass, setting=None):

设置回测的策略


以上就是回测开始前的准备工作,下面就是如何利用历史数据进行回测


loadHistoryData(self):

用于载入历史数据,代码主要涉及pymongo的使用,可自行百度


crossLimitOrder(self):

基于最新数据撮合限价单


crossStopOrder(self):

基于最新数据撮合停止单


上面两个用于撮合成交的类方法代码逻辑类似,源代码的解释很详细,用文字解释反而麻烦多余。


sendOrder,cancelOrder,sendStopOrder,cancelStopOrder,cancelAll

都是策略接口,用于处理订单


newBar(self, bar):

传入bar数据,首先撮合订单,然后调用策略的onBar()方法处理数据,并更新每日收盘价


newTick(self, tick):

与上面类似


runBacktesting(self):

运行回测,逻辑很清晰,载入数据,选择数据类,初始化策略,启动策略,回放数据,结束。


后面的类方法都是依据回测中发生的交易计算结果,不在赘述。



到这里,整个回测的框架就很清楚了,现在根据runBacktesting.py,看看如何运用上面的框架来回测。


runBacktesting.py

现在是要回测策略strategyKingKeltner在IF0000的历史数据上的表现,之前已经通过loadCsv.py把数据导入了数据库。

首先from vnpy.trader.app.ctaStrategy.ctaBacktesting import BacktestingEngine, MINUTE_DB_NAME,用来创建BacktestingEngine的实例,以及连接刚才导入的数据库中的数据

from vnpy.trader.app.ctaStrategy.strategy.strategyKingKeltner import KkStrategy 导入策略

engine = BacktestingEngine()创建回测引擎,然后下一步通过里面的类方法设置你需要的初始化参数,本例中,回测模式为bar模式,然后设置开始时间,滑点等等,接着调用initStrategy方法,在引擎中建立策略的实例。

开始回测,要想了解回测过程中的具体细节,最好的方法是利用pycharm在每个运行到的地方设置断点,一步一步的看,走完整个过程(本来想用文字描述,感觉效率太低,还是请读者自己运行一遍)。

回测结束,看看结果吧。





从策略编写说起

其实到这里已经可以根据前面的内容写策略了,下面就举一个简单的例子。

交易螺纹钢,初始资金1W,只交易一手,最多持仓一手,策略是利用布林通道,上穿买入,下穿卖出,600分钟定时退出,1分钟k线。

第一步:导入数据

vnpy给的示例已经导入了rb0000。

第二步:编写策略

可以模仿vnpy给的示例策略,大致可以摸索出一个策略模板,代码添加了更详细的注释

from __future__ import division

from vnpy.trader.vtObject import VtBarData
from vnpy.trader.vtConstant import EMPTY_STRING
from vnpy.trader.app.ctaStrategy.ctaTemplate import (CtaTemplate,
BarManager,
ArrayManager)
#可以导入自己需要的包

class strategyname(CtaTemplate): #strategyname改成自己命名的策略名称,下面的strategyname同样替换

className = 'strategyname'
author = '' #随意输入# 策略参数,添加需要的参数# 策略变量,添加需要的变量# 参数列表
paramList = ['name','className','author','vtSymbol',]# 变量列表
varList = ['inited','trading','pos']# 列表中已有的都是继承自CtaTemplate
#----------------------------------------------------------------------
def __init__(self, ctaEngine, setting):"""Constructor"""super(strategyname, self).__init__(ctaEngine, setting)  #必须要有的语句self.bm = BarManager(self.onBar, xmin=0, onXminBar=None)        # 创建K线合成器对象,后面两个参数根据需要传入SELF.AM = ArrayManager()# 如果里面的指标不够用需要自己添加# 如果是多合约实例的话,变量需要放在__init__里面,可以参考github的说明#----------------------------------------------------------------------
def onInit(self):  #这里的代码不用更改,直接使用即可"""初始化策略(必须由用户继承实现)"""self.writeCtaLog(u'%s策略初始化' %self.name)# 载入历史数据,并采用回放计算的方式初始化策略数值initData = self.loadBar(self.initDays)for bar in initData:self.onBar(bar)self.putEvent()#----------------------------------------------------------------------
def onStart(self):  #这里的代码不用更改,直接使用即可"""启动策略(必须由用户继承实现)"""self.writeCtaLog(u'%s策略启动' %self.name)self.putEvent()#----------------------------------------------------------------------
def onStop(self):  #这里的代码不用更改,直接使用即可"""停止策略(必须由用户继承实现)"""self.writeCtaLog(u'%s策略停止' %self.name)self.putEvent()#----------------------------------------------------------------------
def onTick(self, tick):  # 如果是tick策略,则策略主体在这里,若不是,实盘时利用下面的类方法合成k线"""收到行情TICK推送(必须由用户继承实现)""" self.bm.updateTick(tick)#----------------------------------------------------------------------
def onBar(self, bar):  # 如果是1分钟k线策略,则策略主体在这里pass#----------------------------------------------------------------------
def onXminbar(self, bar):  # 如果是X分钟k线策略,则策略主体在这里pass#----------------------------------------------------------------------
def onOrder(self, order):"""收到委托变化推送(必须由用户继承实现)"""pass#----------------------------------------------------------------------
def onTrade(self, trade):# 发出状态更新事件self.putEvent()#----------------------------------------------------------------------
def onStopOrder(self, so):"""停止单推送"""pass#----------------------------------------------------------------------
def customized_function(self, *args): #  定制自己的类方法,例如strategyKingKeltner.py中定义的sendOcoOrder()pass</code></pre></div><p>依据上面的内容,这个策略就这样写</p><div class="highlight"><pre><code class="language-text">from __future__ import division

from vnpy.trader.vtObject import VtBarData
from vnpy.trader.vtConstant import EMPTY_STRING
from vnpy.trader.app.ctaStrategy.ctaTemplate import (CtaTemplate,
BarManager,
ArrayManager)

class bollinger(CtaTemplate): #strategyname改成自己命名的策略名称,下面的strategyname同样替换

className = 'bollinger'
author = u'尔鸫' #随意输入# 策略参数,添加需要的参数
boll_window = 600                     # 布林通道窗口数 
boll_dev = 2                          # 布林通道的偏差
leaving_window = 600                  # 定时离开的窗口数
init_days = 10                        # 初始化数据所用的天数
fixed_size = 1                        # 每次交易的数量# 策略变量,添加需要的变量
upper_band = 0                        # 布林通道上轨
lower_band = 0                        # 布林通道下轨
count_num = 0                         # 用于记录成交的k线与当前推送的bar距离多少# 参数列表
paramList = ['name','className','author','vtSymbol','boll_window','boll_dev','leaving_window','init_days','fixed_size']# 变量列表
varList = ['inited','trading','pos','upper_band','lower_band','count_num']#----------------------------------------------------------------------
def __init__(self, ctaEngine, setting):"""Constructor"""super(bollinger, self).__init__(ctaEngine, setting)  self.bm = BarManager(self.onBar, xmin=0, onXminBar=None)        SELF.AM = ArrayManager(1000)#----------------------------------------------------------------------
def onInit(self):  """初始化策略(必须由用户继承实现)"""self.writeCtaLog(u'%s策略初始化' %self.name)# 载入历史数据,并采用回放计算的方式初始化策略数值initData = self.loadBar(self.init_days)for bar in initData:self.onBar(bar)self.putEvent()#----------------------------------------------------------------------
def onStart(self):  #这里的代码不用更改,直接使用即可"""启动策略(必须由用户继承实现)"""self.writeCtaLog(u'%s策略启动' %self.name)self.putEvent()#----------------------------------------------------------------------
def onStop(self):  #这里的代码不用更改,直接使用即可"""停止策略(必须由用户继承实现)"""self.writeCtaLog(u'%s策略停止' %self.name)self.putEvent()#----------------------------------------------------------------------
def onTick(self, tick):  # 如果是tick策略,则策略主体在这里,若不是,利用下面的类方法合成k线"""收到行情TICK推送(必须由用户继承实现)""" self.bm.updateTick(tick)#----------------------------------------------------------------------
def onBar(self, bar):  # 如果是1分钟k线策略,则策略主体在这里# 全撤之前发出的委托self.cancelAll()# 保存K线数据am = SELF.AMam.updateBar(bar)if not am.inited:return# 计算指标数值self.upper_band, self.lower_band = am.boll(self.boll_window, self.boll_dev)self.count_num += 1if self.pos == 0:if bar.close &gt; self.upper_band:self.buy(bar.close+5, self.fixed_size)self.count_num = 0elif bar.close &lt; self.lower_band:self.short(bar.close-5, self.fixed_size)self.count_num = 0if self.pos &gt; 0:if self.count_num == self.leaving_window:self.sell(bar.close-10,abs(self.pos))elif self.pos &lt; 0:if self.count_num == self.leaving_window:self.cover(bar.close+10,abs(self.pos))self.putEvent()#----------------------------------------------------------------------
def onXminbar(self, bar):  # 如果是X分钟k线策略,则策略主体在这里pass#----------------------------------------------------------------------
def onOrder(self, order):"""收到委托变化推送(必须由用户继承实现)"""pass#----------------------------------------------------------------------
def onTrade(self, trade):# 发出状态更新事件self.putEvent()#----------------------------------------------------------------------
def onStopOrder(self, so):"""停止单推送"""pass#----------------------------------------------------------------------
def customized_function(self, *args): #  定制自己的类方法,例如strategyKingKeltner.py中定义的sendOcoOrder()pass</code></pre></div><p>第三步:编写runbacktesting</p><div class="highlight"><pre><code class="language-text">from __future__ import division

from vnpy.trader.app.ctaStrategy.ctaBacktesting import BacktestingEngine, MINUTE_DB_NAME

if name == ‘main’:
from bollinger import bollinger #

# 创建回测引擎
engine = BacktestingEngine()# 设置引擎的回测模式为K线
engine.setBacktestingMode(engine.BAR_MODE)# 设置回测用的数据起始日期
engine.setStartDate('20110104')# 设置产品相关参数
engine.setCapital(10000)
engine.setSize(10)
engine.setSlippage(1)     # 股指1跳
engine.setRate(3/10000)   # 万0.3 
engine.setPriceTick(1)    # 股指最小价格变动# 设置使用的历史数据库
engine.setDatabase(MINUTE_DB_NAME, 'rb0000') #[IF0000, rb0000]# 在引擎中创建策略对象
d = {}
engine.initStrategy(bollinger, d)# 开始跑回测
engine.runBacktesting()# 显示回测结果
engine.showBacktestingResult()</code></pre></div><p>第四步:结果</p><figure data-size="normal"><noscript>&lt;img src="https://pic1.zhimg.com/v2-435737a755dd9cde69b64bd9523b2c88_b.jpg" data-caption="" data-size="normal" data-rawwidth="1000" data-rawheight="1246" class="origin_image zh-lightbox-thumb" width="1000" data-original="https://pic1.zhimg.com/v2-435737a755dd9cde69b64bd9523b2c88_r.jpg"/&gt;</noscript><img src="https://pic1.zhimg.com/80/v2-435737a755dd9cde69b64bd9523b2c88_1440w.jpg" data-caption="" data-size="normal" data-rawwidth="1000" data-rawheight="1246" class="origin_image zh-lightbox-thumb lazy" width="1000" data-original="https://pic1.zhimg.com/v2-435737a755dd9cde69b64bd9523b2c88_r.jpg" data-actualsrc="https://pic1.zhimg.com/v2-435737a755dd9cde69b64bd9523b2c88_b.jpg" data-lazy-status="ok"></figure><p>最后的回撤是因为设置的600根k线退出,而数据结尾不够600。</p><p class="ztext-empty-paragraph"><br></p><p>这样,整个vnpy策略编写的指南大致成型,利用上面的内容基本可以进行自己的研究了。</p><p class="ztext-empty-paragraph"><br></p><p>当然,这篇文章还远没有结束,仍有地方未探索,我会继续把想法更新上来。</p><p class="ztext-empty-paragraph"><br></p><p>未完待续...</p></div>

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/479912.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

掌握神经网络,我应该学习哪些至关重要的知识点?

人工智能作为计算机科学领域的一个分支&#xff0c;在互联网和大数据的时代浪潮中显现出其巨大的潜力和蓬勃的活力&#xff0c;类似电子医生、无人驾驶等新名词纷纷涌现。人工智能凭借着它无与伦比的发展优势&#xff0c;推动了各大产业和技术的革命与创新&#xff0c;使得生产…

LeetCode 32. 最长有效括号(栈DP)

文章目录1. 题目信息2. 栈 解题3. 动态规划 解题1. 题目信息 给定一个只包含 ‘(’ 和 ‘)’ 的字符串&#xff0c;找出最长的包含有效括号的子串的长度。 示例 1:输入: "(()" 输出: 2 解释: 最长有效括号子串为 "()" 示例 2:输入: ")()())" 输…

消息中间件系列(四):消息队列MQ的特点、选型、及应用场景详解

前面集中谈了分布式缓存Redis系列&#xff1a; 高并发架构系列&#xff1a;分布式锁的由来、特点、及Redis分布式锁的实现详解 高并发架构系列&#xff1a;Redis并发竞争key的解决方案详解 高并发架构系列&#xff1a;Redis缓存和MySQL数据一致性方案详解 Redis的高可用详解…

基金定投

https://www.zhihu.com/question/19909886 相信我&#xff0c;这篇攻略能让你彻底搞懂基金&#xff0c;每年大概率能赚10%左右的收益&#xff01;基金定投核心要搞懂两个问题&#xff1a;买什么基金&#xff0c;什么时候买。今天我给大家一篇文章讲透这两个问题&#xff01;我说…

卖萌屋学术站发布!通往高效刷论文之路

文 | 夕小瑶编 | 兔子酱学术站诞生好久没有冒泡啦&#xff0c;大家还记得雁栖湖畔的夕小瑶吗&#xff01;&#xff08;划掉(*/ω&#xff3c;*)趁着国庆假期&#xff0c;跟卖萌屋小伙伴们终于把拖延已久的《Arxiv神器》翻新了&#xff0c;零零星星做了几个月&#xff0c;最近终…

消息中间件系列(五):MQ消息队列的12点核心原理总结

消息队列已经逐渐成为分布式应用场景、内部通信、以及秒杀等高并发业务场景的核心手段&#xff0c;它具有低耦合、可靠投递、广播、流量控制、最终一致性 等一系列功能。 无论是 RabbitMQ、RocketMQ、ActiveMQ、Kafka还是其它等&#xff0c;都有的一些基本原理、术语、机制等&…

征稿 | 2019年全国知识图谱与语义计算大会(CCKS2019)投稿时间延长

全国知识图谱与语义计算大会&#xff08;CCKS: China Conference on Knowledge Graph and Semantic Computing&#xff09;由中国中文信息学会语言与知识计算专业委员会组织和承办。全国知识图谱与语义计算大会是两个全国性会议的合并&#xff1a;中文知识图谱研讨会the Chines…

LeetCode 641. 设计循环双端队列

文章目录1. 题目信息2. 解题1. 题目信息 设计实现双端队列。 你的实现需要支持以下操作&#xff1a; MyCircularDeque(k)&#xff1a;构造函数,双端队列的大小为k。 insertFront()&#xff1a;将一个元素添加到双端队列头部。 如果操作成功返回 true。 insertLast()&#xff…

代码逆流成河,深入C++如何又快又有效?

虽然编程语言有很多&#xff0c;但在需要顶级性能的项目上&#xff0c;基本都会使用C。尤其是机器人、自动驾驶、AI等嵌入和实时系统&#xff0c;都是C的主要应用方向&#xff0c;在这种层面&#xff0c;几乎没有竞争者。比如熊厂的搜索引擎、推荐引擎等核心产品&#xff0c;鹅…

论文浅尝 | 混合注意力原型网络的含噪音少样本的关系分类

论文笔记整理&#xff1a;余海阳&#xff0c;浙江大学硕士&#xff0c;研究方向为知识图谱、自然语言信息抽取。链接&#xff1a;https://www.aaai.org/Papers/AAAI/2019/AAAI-GaoTianyu.915.pdf动机现有的关系分类方法主要依赖于远程监控&#xff08;DS&#xff09;&#xff0…

消息中间件系列(七):如何从0到1设计一个消息队列中间件

消息队列作为系统解耦&#xff0c;流量控制的利器&#xff0c;成为分布式系统核心组件之一。 如果你对消息队列背后的实现原理关注不多&#xff0c;其实了解消息队列背后的实现非常重要。 不仅知其然还要知其所以然&#xff0c;这才是一个优秀的工程师需要具备的特征。 今天…

LeetCode 239. 滑动窗口最大值(双端队列+单调栈)

文章目录1. 题目信息2. 解题2.1 暴力法2.2 双端队列法1. 题目信息 给定一个数组 nums&#xff0c;有一个大小为 k 的滑动窗口从数组的最左侧移动到数组的最右侧。你只可以看到在滑动窗口内的 k 个数字。滑动窗口每次只向右移动一位。 返回滑动窗口中的最大值。 示例:输入: n…

Airbnb搜索:重排序阶段如何优化搜索结果多样性?

文 | 谷育龙Eric编 | QvQ我是谷育龙Eric&#xff0c;研究方向有深度学习、搜索推荐&#xff0c;喜欢为大家分享深度学习在搜索推荐广告排序应用的文章。本文将基于Airbnb KDD 2020年的论文&#xff0c;介绍Airbnb搜索排序中在重排序阶段如何解决多样性的问题&#xff0c;对工业…

肖仰华 | 做个“有知识”的机器人

本文转载自公众号:知识工场。肖仰华博士&#xff0c;复旦大学计算机科学与技术学院教授&#xff0c;博士生导师&#xff0c;知识工场实验室负责人。本文是肖仰华教授应《中国计算机学会通信》邀请所撰写的特邀文章&#xff0c;全文见 CCCF 2019 年第 5 期。摘要&#xff1a;时下…

消息中间件系列(九):详解RocketMQ的架构设计、关键特性、与应用场景

内容大纲&#xff1a; RocketMQ的简介与演进 RocketMQ的架构设计 RocketMQ的关键特性 RocketMQ的应用场景 RocketMQ的简介 RocketMQ一个纯java、分布式、队列模型的开源消息中间件&#xff0c;前身是MetaQ&#xff0c;是阿里研发的一个队列模型的消息中间件&#xff0c;后开…

LeetCode 151. 翻转字符串里的单词(栈)

文章目录1. 题目信息2. 解题1. 题目信息 给定一个字符串&#xff0c;逐个翻转字符串中的每个单词。 示例 1&#xff1a;输入: "the sky is blue" 输出: "blue is sky the" 示例 2&#xff1a;输入: " hello world! " 输出: "world! hel…

推荐系统架构与算法流程详解

文 | yijiapan腾讯 WXG 数据分析师推荐算法的理解如果说互联网的目标就是连接一切&#xff0c;那么推荐系统的作用就是建立更加有效率的连接&#xff0c;推荐系统可以更有效率的连接用户与内容和服务&#xff0c;节约了大量的时间和成本。如果把推荐系统简单拆开来看&#xff0…

论文浅尝 | 将字面含义嵌入知识图谱表示学习

论文笔记整理&#xff1a;吴桐桐&#xff0c;东南大学博士生&#xff0c;研究方向为知识图谱&#xff0c;自然语言处理。链接&#xff1a;https://arxiv.org/pdf/1802.00934.pdf本文主要关注知识图谱中的链接预测问题&#xff0c;在既有的知识图谱表示学习模型的基础上提出了一…

优知学院创始人陈睿:怎样做好一个创业公司CTO?

CTO 是企业内技术最高负责人&#xff0c;对企业的发展起到至关重要的作用。但随着公司的不断发展&#xff0c;CTO 的工作重心也会不断变化。只有在正确的阶段做正确的事&#xff0c;才能更好地为公司做出贡献。 本文作者&#xff1a;陈睿 优知学院创始人&#xff0c;10年产品技…

2020年深度学习调参技巧合集

文 | 山竹小果源 | NewBeeNLP编 | 夕小瑶的卖萌屋重点说明&#xff1a;本文主要为整理总结&#xff0c;大部分参考文末资料&#xff0c;感谢分享。寻找合适的学习率学习率是一个非常非常重要的超参数&#xff0c;这个参数呢&#xff0c;面对不同规模、不同batch-size、不同优化…