中高频多因子库存储最佳实践

news2024/12/1 0:35:53

 1. 概述

因子挖掘是量化交易的基础。随着量化交易竞争的加剧,量化投资团队需要处理大量因子。在许多情况下,因子数据量甚至会远远超过高频的行情数据量。以 5,000 只股票 10,000 个因子为例,一年的 10 分钟线数据量为 2.3TB,1分钟线数据量为 23TB,3秒线数据量为 460 TB。如此量级的数据就对因子存储方案提出了很高的要求。

本文将基于中高频多因子存储场景,结合实际数据案例来设计 DolphinDB 存储方案,并对比不同存储模式下的性能,给出最佳存储模式的建议。

1.1 中高频多因子存储面临的挑战

在数据高频次和因子高数量的双重叠加之下,数据量将轻易达到 TB 级别,那么中高频多因子的存储方案就必须同时面对以下问题:

  • 庞大的数据量

因子计算通常有4个维度包括股票、因子、频率和时间。国内股票总个数按5,000来算。因子个数一般机构大约为1,000起,多的甚至有10,000个因子。时间频率最高的是每3秒钟生成一次数据,频率低的也有10分钟一次,也就是说,一只股票一个因子一天会生成24到4,800条 tick 数据。

宽表存储模式数据量统计:

股票(只)因子(个)频率时间数据量(GB)
5,00010,00010 分钟线4 年9,014
5,0001,0001 分钟线2 年4,513
5,0001,0003 秒线1 年45,129

窄表存储模式数据量统计:

股票(只)因子(个)频率时间数据量(GB)
5,00010,00010 分钟线4 年27,037
5,0001,0001 分钟线2 年13,518
5,0001,0003 秒线1 年134,183

面对如此庞大的数据量,如何保证高效的数据写入和数据压缩是因子库存储的一大挑战,如果不能支持并充分发挥多块磁盘的 I/O,写入耗时将达数小时以上。

  • 因子库动态变化

因子库经常会发生变化,往往需要新增因子、修改因子定义,或加入新的股票等。面对 TB 级的因子数据,单个因子的新增、修改、删除耗时应该保证在秒级才能确保量化投研的效率。

对于上述问题,我们在设计方案时,除了尽可能优化每一项操作的性能,更重要的是每项性能不能出现明显短板,否则在数据操作量级大幅上升后,会大幅度降低整体的生产效率。

  • 多因子对齐输出

金融行业的数据分析通常需要数据支持面板格式。对于读取随机标的(A 股市场目前约5,000只股票)、随机多个因子(10,000个因子中随机查询1,000个因子)的场景,需要在操作时能够尽可能高速并精准读取数据,减少无效 I/O ,并以需要的方式(通常是因子面板模式)将数据读取出来。

1.2 中高频多因子存储场景需求

总体来看,中高频多因子的存储方案需要满足以下几点:

  • 保证写入高效性;
  • 支持高效的因子库运维(新增因子及因子数据的修改、删除);
  • 支持高效、灵活的读取方式,且以最适合金融计算的方式输出。

2. DolphinDB 的存储特性

面对中高频多因子存储场景,我们将 DolphinDB 的下述存储特性组合使用,提供了灵活的存储解决方案。

2.1 分区存储

数据分区存储,针对因子数据可以采用“时间+因子/标的”的方式组合分区来达成因子数据的灵活分区存储。DolphinDB 对于不同分区数据可以多线程并行操作。

2.2 分区内分组排序存储

DolphinDB 的 TSDB 引擎提供排序键设置,对分区内的数据完成分块索引。这样的设置有助于在随机读时更精准的定位数据。例如,在“2022年2月-factor1”的分区内部,数据可以按照 SortColumn-SortKey 字段 SecurityID 分组存储,在每个 SecurityID 组内,再按照时间字段 TradeTime 排序,如下图所示。

借助以上特性,可以灵活地设计存储方案,以应对中高频多因子场景下的不同需求。

下文中,我们以一个“10分钟级100000因子”的例子,为大家测试在不同存储模式下,数据的写入、查询和运维等方面的性能,并通过分析结果,为大家提供一个中高频多因子库存储的最佳实践。

3. 十分钟级一万因子存储场景解决方案

3.1 存储方案设计

在存储因子数据时,用户可以选择窄表和宽表两种模式。

窄表模式一般有4列:时间戳、股票代码、因子编号以及因子值,如下图所示。在需要面板数据的场景中,窄表模式的数据可使用 DolphinDB 的 pivot 功能转换为面板数据。面对金融场景时序数据中大量因子需要对齐转置的情形,可以根据时间、股票代码和因子对数据表重新排序,将时间和股票代码作为行,因子作为列进行计算输出,并且非常高效。

宽表模式中,一般每个因子存为一列,如下图所示。宽表模式下的面板数据可以直接用于量化交易中的程序计算,符合金融场景的数据输出需求,但在测试案例一三种运维操作的测试数据对比中,我们会看到,因子数据的新增和修改场景下宽表耗时较高。

DolphinDB 中同时支持宽表和窄表的两种模式数据存储。结合 DolphinDB 的存储特性,我们设计以下两种存储方案来比对10分钟级10,000因子场景存储性能:

方案 1:窄表模式

  • TradeTime 按月值分区 + FactorName 值分区
  • 排序字段: SecurityID + TradeTime

把时间分区调整到月,对因子分区调整到每个因子单独分区,并对每个分区内的数据按照 SecurityID 分组,组内按照 TradeTime 排序。这样的好处是每个分区数据大小适合,在数据检索时,既可以按照时间和因子名进行分区剪枝干,又可以按照股票 ID 近一步的精确定位数据,满足在随意组合因子、标的场景下精准地读取数据。

方案 2:宽表模式

  • TradeTime 按月值分区 + SecurityID 值分区
  • 排序字段: SecurityID + TradeTime

在 SecurityID 上进行分区剪枝,因子维度上通过选择不同的列来进行数据筛选。

下文将在固态硬盘(SSD)和机械硬盘(HDD)这两种不同的硬件配置下,对宽表和窄表的存储性能分别进行测试。

3.2 数据准备

我们通过模拟随机生成5,000只股票10分钟级10,000个因子的数据,并分别采用窄表和宽表两种方式来存储。

随机生成因子名称和股票代码的函数定义如下:

def createFactorNamesAndSymbolNamse(num_factors,num_symbols){
	factor_names = lpad(string(1..num_factors),6,"f00000")
	symbols_preliminary = lpad(string(1..num_symbols),6,"000000")+"."
	areas = rand(["SZ","SH"],num_symbols)
	symbols = symbols_preliminary + areas
	return factor_names,symbols
}

生成字段及字段类型的函数定义如下:

def createColnameAndColtype(mode,factor_names){
	if(mode == "single"){
		return ["tradetime","symbol","factorname","value"],[DATETIME,SYMBOL,SYMBOL,DOUBLE]
	}else{
		col_names = ["tradetime","symbol"].append!(factor_names)
		col_types = [DATETIME,SYMBOL].append!(take(DOUBLE,factor_names.size()))
		return col_names,col_types
	}
}

3.3. 测试案例一:HDD 存储

服务器配置:

  • CPU:64核
  • 内存:512G
  • 磁盘:9块 HDD 硬盘
  • 数据库设置:单机集群三数据节点

因子写入

对于5,000只股票10,000个因子,我们首先测试写入2022.1.1至2022.1.31内的10 钟级数据,可以运行如下代码定义不同存储模式下的因子写入函数:

// 窄表模式写入某个时间范围数据
def writeSingleModelData(dbname,tbname,start_date,end_date,symbols,factor_names){
	total_time_range = getTimeList(start_date,end_date)
	nodes = exec value from pnodeRun(getNodeAlias)
	for(j in 0..(total_time_range.size()-1)){
		for(i in 0..(factor_names.size()-1)){
			rpc(nodes[i%(nodes.size())],submitJob,"singleModel"+j+"and"+i,dbname,singleModelPartitionData,dbname,tbname,total_time_range[j],symbols,factor_names,factor_names[i])
		}
	}
}

// 宽表模式写入某个时间范围数据
def writeWideModelData(dbname,tbname,start_date,end_date,symbols,factor_names){
	total_time_range = getTimeList(start_date,end_date)
	nodes = exec value from pnodeRun(getNodeAlias)
	for(j in 0..(total_time_range.size()-1)){
		for(i in 0..(symbols.size()-1)){
			rpc(nodes[i%(nodes.size())],submitJob,"wideModel"+j+"and"+i,dbname,wideModelPartitionData,dbname,tbname,total_time_range[j],factor_names,symbols[i])
		}
	}
}

可以看到,宽表模式在数据写入速度上优于窄表模式,硬盘占用空间上略优于窄表模式。这是因为窄表模式下的数据冗余度高,实际数据量比较大。另外需要说明一点,实验中因子值使用的是随机的浮点数,几乎没有重复,压缩比较低,实际场景中的压缩比会更高。

存储方案写入天数每天行数总行数每行字节数据原始大小 (GB)落盘大小 (GB)写入耗时 (s)压缩比磁盘 I/O(MB/s)
宽表21120,0002,520,00080,0121901663011.1648
宽表21120,0002,520,00080,0121901663011.1648

因子查询

查询21天全市场5,000只标的的1,000个因子数据,窄表的查询会将数据转换成与宽表一样的面板数据输出。定义因子查询函数的核心代码如下:

// 窄表模式查询随机1000因子
def querySingleModel(dbname,tbname,start_time,end_time,aim_factor){
	return select value from loadTable(dbname,tbname) where tradetime>=start_time and tradetime<= end_time and  factorname in aim_factor pivot by tradetime,symbol,factorname
}

// 宽表模式查询随机1000因子
def queryWideModel(dbname,tbname,start_time,end_time,aim_factor){
	ll = aim_factor[0]
	for(i in 1..(aim_factor.size()-1)){
		ll = ll+","+aim_factor[i]
	}
	script = "select tradetime,symbol,"+ll+"from loadTable("+'"'+dbname+'"'+","+'"'+tbname+'"'+")" + "where tradetime>="+start_time+"and tradetime<="+end_time
	tt = parseExpr(script).eval()
	return tt
}

数据以宽表模式存储在机械硬盘中时,对10,000个因子随机查询1,000个因子的初次查询速度慢一些;查询前1,000个因子则速度较快。这是因为在机械硬盘下进行多列的随机检索会比较慢。

即便如此,与宽表模式的最快情况相比,窄表模式下经过 pivot by 转为面板数据后的查询速度也要更快。这是因为虽然宽表的所有列已经按照面板模式准备好,但是宽表的数据都是在同一个分区,读取时是单线程进行。而窄表模式数据存在于很多因子分区 ,读取数据和拼接面板数据时是很多个 CPU 同时工作,这是个分布式多线程操作,所以窄表模式在查询面板数据时耗时更少。

存储方案因子选择数据大小 (GB)冷查询耗时 (s)热查询耗时 (s)
窄表随机 1,00018.88052
宽表随机 1,00018.8
宽表窄表18.842549

数据运维

因子数据的运维包括新增因子、更新因子、删除因子。

  • 新增因子

在新增因子的场景,窄表模式可以使用 append! 插入新的因子数据;而宽表模式需要先进行 addColumn 操作,然后通过 update 操作更新因子列数据。在 DolphinDB 当前的设计下,更新宽表模式中某一列因子,需要将分区数据全部重写,耗时较长

假设此处需要新增第 f10002 号因子在2022.1.1至2022.1.31时间范围内的数据,不同存储模式下的新增因子脚本如下所示:

//窄表模式新增1个因子
def singleModelAddNewFactor(dbname,tbname,start_date,end_date,symbols,factor_names,new_factor){
	time_list = getTimeList(start_date,end_date).flatten()
	num_row = symbols.size()*time_list.size()
	col_names,col_types = createColnameAndColtype("single",factor_names)
	t = table(num_row:num_row,col_names,col_types)
	t["tradetime"] = stretch(time_list,num_row)
	t["symbol"] = take(symbols,num_row)
	t["factorname"] = take(new_factor,num_row)
	t["value"] = rand(100.0,num_row)
	pt = loadTable(dbname,tbname)
	pt.append!(t)	
}

//宽表模型新增一个因子
def wideModelAddNewFactor(dbname,tbname,start_date,end_date,symbols,new_factor,parallel = true){   //parallel=true表示并行,=false表示串行
	pt = loadTable(dbname,tbname)
	addColumn(pt,[new_factor],[DOUBLE])
	time_list = getTimeList(start_date,end_date)
	start_time_list,end_time_list = [],[] 
	for(i in 0..(time_list.size()-1)){
		start_time_list.append!(time_list[i][0])
		idx = time_list[i].size()-1
		end_time_list.append!(time_list[i][idx])
	}
	if(!parallel){
		for(i in 0..(start_time_list.size()-1)){
			for(j in 0..(symbols.size()-1)){
				wideModelSinglePartitionUpdate(dbname,tbname,start_time_list[i],end_time_list[i],new_factor,symbols[j])
			}
		}
	}else{
		for(i in 0..(start_time_list.size()-1)){
			ploop(wideModelSinglePartitionUpdate{dbname,tbname,start_time_list[i],end_time_list[i],new_factor,},symbols)
		}
	}
}
  • 更新因子

量化投研中,重新计算因子数据是常见的场景。根据窄表模式下的分区规则,对指定因子数据更新时,可以精确定位到因子所在分区,并进行修改,所以耗时在秒级;而宽表模式的更新方式如上节所述原因,耗时较长。

假定此处需要更新第 f00555 号因子在2022.1.1至2022.1.31时间范围内的因子值数据,不同存储模式下的脚本如下所示:

//窄表模式更新1个因子
def singleModelUpdateFactor(dbname,tbname,start_date,end_date,update_factor,parallel = false){   //parallel=true表示并行更新
	time_list = getTimeList(start_date,end_date)
	start_time_list,end_time_list = [],[] 
	for(i in 0..(time_list.size()-1)){
		start_time_list.append!(time_list[i][0])
		idx = time_list[i].size()-1
		end_time_list.append!(time_list[i][idx])
	}
	if(!parallel){
		for(i in 0..(start_time_list.size()-1)){
			singleModelSinglePartitionUpdate(dbname,tbname,start_time_list[i],end_time_list[i],update_factor)
		}		
	}else{
		ploop(singleModelSinglePartitionUpdate{dbname,tbname,,,update_factor},start_time_list,end_time_list)
	}
}

//宽表模型更新1个因子
def wideModelUpdateFactor(dbname,tbname,start_date,end_date,update_factor,symbols,parallel = true){  //parallel=true表示并行更新,=false表示串行
	time_list = getTimeList(start_date,end_date)
	start_time_list,end_time_list = [],[] 
	for(i in 0..(time_list.size()-1)){
		start_time_list.append!(time_list[i][0])
		idx = time_list[i].size()-1
		end_time_list.append!(time_list[i][idx])
	}
	if(!parallel){
		for(i in 0..(start_time_list.size()-1)){
			for(j in 0..(symbols.size()-1)){
				wideModelSinglePartitionUpdate(dbname,tbname,start_time_list[i],end_time_list[i],update_factor,symbols[j])	
			}
		}
	}else{
		for(i in 0..(start_time_list.size()-1)){
			ploop(wideModelSinglePartitionUpdate{dbname,tbname,start_time_list[i],end_time_list[i],update_factor,},symbols)
		}
	}
}
  • 删除因子

删除因子虽然不是必须的,但可以释放存储空间,以及提供其他便利。当前窄表模型的分区方案在删除指定因子时耗时在秒级,脚本如下所示,TSDB 引擎下的宽表模式目前不支持删除因子列。

// 单值模型删除一个因子
def singleModelDeleteFactor(dbname,tbname,start_date,end_date,delete_factor){
	pt = loadTable(dbname,tbname)
	time_list = getTimeList(start_date,end_date).flatten()
	start_time,end_time = time_list[0],time_list[time_list.size()-1]
	delete  from pt where tradetime >= start_time and tradetime <= end_time and factorname = delete_factor
}

三种运维操作下的测试数据如下表所示,可以看到在10分钟级10,000个因子数据场景下,窄表模式在因子数据查询和因子数据运维方面全面优于宽表模式,只是在数据写入速度和存储空间要逊于宽表模式。综合考虑各个方面,使用窄表模式存储因子数据是更好的选择。

数据运维操作窄表 (s)宽表 (s)
新增 1 因子1.2534
更新 1 因子1.1541
删除 1 因子0.8N/A

3.4. 测试案例二:SSD 存储

上一节中,我们使用机械硬盘比对了一个月因子数据场景下宽表和窄表的性能。在实际生产时,为了提高效率,我们往往选择 SSD 硬盘来存储因子数据。本小节我们就选择 SSD 硬盘来进行测试,进行6个月因子数据场景下宽表和窄表的性能比对。同样从因子写入、因子查询和数据运维三个方面进行测试。

服务器配置:

  • CPU 48 核
  • 内存:512G
  • 磁盘:4 块 SSD 硬盘
  • 数据库设置:单机集群二数据节点

因子写入

通过多任务并行方式写入6个月5,000只标的10分钟级10,000 子数据,定义因子写入函数的代码与案例一中因子写入一致,完整脚本可参考附件。从结果我们可以看到,宽表模式在写入速度和存储空间上性能占优。

存储方案写入天数每天行数总行数每行字节数据原始大小 (GB)落盘大小 (GB)写入耗时 (s)压缩比磁盘 I/O(MB/s)
方案 1 - 窄表1291,200,000,000154,800,000,000242,8731,1182,1942.61,338
方案 2 - 宽表129120,00015,480,00080,0121,1431,0301,1151.11,049

因子查询

接下来我们随机查询5,000只标的下1,000个因子在1, 3, 6个月内的数据,核心查询代码与案例一中一致,完整脚本可参考附件。

测试结果如下所示,其中冷查询表示在无数据缓存的情况下进行查询,热查询表示有数据缓存的情况下进行查询 。可以看到,在使用 SSD 磁盘的情况下,窄表模式的查询耗时同样低于宽表模式。此外随着查询数据量增长,查询耗时是线性增长的,不会因为查询数据量的大增而出现查询耗时大幅增加的情况。

存储方案查询数据大小 (GB)查询数据月份冷查询耗时 (s)热查询耗时 (s)
宽表18.815943
宽表18.835943
窄表57.3310390
宽表57.33171115
窄表115.56201173
宽表115.56363274

数据运维

同样,我们从新增、更新和删除因子三个角度,测试1, 3, 6个月的数据运维性能,核心代码与案例一中一致,完整脚本可参考附件。在这个环节窄表模式同样远远优于宽表模式,且数据运维的各项操作,耗时同样随操作影响数据量线性增长。

数据运维操作操作因子数据月份窄表 (s)宽表 (s)
新增 1 因子11.0185
新增 1 因子32.9502
新增 1 因子65.81,016
更新 1 因子11.1162
更新 1 因子33.1477
更新 1 因子66.2948
删除 1 因子10.8N/A
删除 1 因子31.1N/A
删除 1 因子61.2N/A

吞吐量

在案例一中我们提到,窄表模式下查询面板数据时,任务会以分布式多线程的方式处理,这将消耗较多的 CPU 资源。

为了验证在多线程并发查询因子数据时,查询性能是否会因为 CPU 资源竞争大幅下降,我们进行了并发查询测试。

我们采取并发8个线程查询一个月全市场5,000只股票、随机10,000个因子数据,每个查询数据大小为 19GB,测试结果如下表。

存储方案总耗时(s)
窄表247
宽表242

通过比对我们可以看到,在8个并发查询的场景,窄表模式的查询确实有一定下降,但总耗时仍然可以达到和宽表模式基本相同的水准。

本节在 SSD 硬盘上测试了6个月的因子数据场景。使用合理设计的窄表模式存储 TB 级别的因子数据,在数据写入、多因子随机查询、因子数据运维等各方面均有稳定表现。在8 程并发情况下查询速度与宽表模式相当。此外,查询、因子运维耗时能够保证线性增长,不会因为海量数据不断增加而出现查询、因子运维耗时大幅增加情况,这样的特性是数据库可以支持海量数据的重要保证。

3.5. 中高频多因子库存储的最佳实践:SSD vs HDD

前两节中我们分别在 HDD 和 SSD 两种硬盘环境下进行了宽表和窄表的存储性能比对。但是在其他系统资源一样的情况下,HDD 和 SSD 的差别是否很大,也是我们比较关心的一件事情。本小节我们将在下表所示的两种硬件配置环境下,对比测试不同存储模式的性能,为大家提供中高频多因子库存储的最佳实践建议。

服务器配置项SSDHDD
CPU64 核64 核
内存512G512G
磁盘3 块 SSD 硬盘(1.5GB/s 吞吐量)9 块 HDD 硬盘(1.4GB/s 吞吐量)
数据库设置单机集群三数据节点单机集群三数据节点

因子写入

写入一个月5,000只标的10钟级10,000因子数据,定义因子写入函数的代码与案例一中一致,完整脚本可参考附件。在磁盘总 I/O 相近的情况,SSD 硬盘的写入速度略优于 HDD 硬盘。

存储方案存储配置数据原始大小 (GB)落盘大小 (GB)压缩比磁盘 IO(MB/s)写入耗时 (s)
SSD窄表4791862.61,068459
SSD宽表1911661.1757257
HDD窄表4791862.6937523
HDD宽表1911661.1648301

因子查询

分别进行查询一个月全市场5,000只股票 随机1,000个因子数据,查询数据量大小 19G,核心查询代码与案例一中一致,完整脚本可参考附件。从下表中可以看到,使用 HDD 硬盘的冷查询和热查询会相差大一些。而无论 SSD 硬盘还是 HDD 硬盘,窄表模式的分布式多线程查询都可以保持更加稳定的性能。

存储方案存储配置冷查询耗时 (s)热查询耗时 (s)
SSD窄表6461
SSD宽表16255
HDD宽表42549
HDD窄表8052

数据运维

在运维场景下,窄表模式的落盘后数据量为 186GB ,宽表为 166GB,新增、更新和删除因子的核心代码与案例一中一致,完整脚本可参考附件。测试结果如下所示,可以看到无论是 SSD 还是 HHD ,窄表模式的运维操作耗时都非常低,基本都是秒级。而宽表模式虽然在 SDD 硬盘下耗时要少于 HDD 硬盘,但总体耗时仍然非常高。

存储方案数据运维操作窄表 (s)宽表 (s)
SSD新增 1 因子1.1330
SSD更新 1 因子0.9316
SSD删除 1 因子0.5N/A
HDD新增 1 因子1.2534
HDD更新 1 因子1.1541
HDD删除 1 因子0.8N/A

从上述结果我们可以看到。在同等条件下,选择 SSD 磁盘在写入场景、冷查询场景性能都要好于 HDD 磁盘。而在因子运维场景,窄表模式耗时比较短,相差并不明显。宽表模式则有较大差异。也就是说在因子数据使用的各个场景采用 SSD 磁盘都可以获得更好的性能。故在因子数据存储上,我们推荐使用 SSD 磁盘来进行因子数据存储。

总结

本教程通过一个“10分钟级10,000因子数据”的例子,测试了因子在不同存储模式(宽表、窄表)和硬盘选择(HDD, SSD)下的写入、新增、更新、删除,以及多线程并发查询时的性能。

通过测试结果可知:在10分钟级10,000因子场景下,采取按月 Value 分区 + 因子名 Value 分区,以及 SortColumn 为 SecurityID+TradeTime 设置下的窄表模式对数据进行存储,为最佳解决方案。

虽然在并发查询场景下,窄表模式的查询操作会因为分布式处理导致的 CPU 资源竞争而增加耗时,但仍可以保证与宽表模式的查询耗时差距不超过5%。

同时,该存储方案无论在 HDD 硬盘或 SSD 硬盘中,都能保持稳定的性能,查询场景性能优于宽表模式,因子运维场景下优势更加明显,各项指标均达到秒级,仅在数据写入环节的性能略逊于宽表模式。

因此,在中高频多因子库的存储方案选择中,我们更推荐用户采用合理设计存储模型的窄表模式。

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

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

相关文章

卡塔尔世界杯出现了半自动越位识别技术、Feelix Palm、动作轨迹捕捉等黑科技,一起来看看吧。

1.史上最快比赛用球 本届世界杯的官方比赛用球名为“旅程&#xff08;Al Rihla&#xff09;”&#xff0c;由于重量很轻&#xff0c;因此在空中的飞行速度比以往任何一届世界杯的比赛用球都快。 “旅程”的球体表面由20个名为SPEEDSHELL的纹理聚氨酯球面材料模块组成&#xf…

一文看懂卷积运算(convolution)与互相关运算(cross-correlation)的区别

目录 互相关运算定义 互相关运算图示 互相关运算完整计算示例 卷积数学定义 卷积运算图示 卷积与互相关运算区别 深度学习中的卷积为何能用互相关运算代替 互相关运算定义 在二维互相关运算中&#xff0c;卷积窗口从输入数组的最左上方开始&#xff0c;按从左往右、从上…

粤嵌实训(笔记)

目录 1. LCD换自己喜欢的颜色 2. LCD换个图案 3. LCD换张图片 4.网线登录 ifconfig eth0 192.168.5.9 5.触屏电子相册 6.网络编程(TCP通信) 7.网络编程(UDP通信) 说实话&#xff0c;对于这个粤嵌的实训&#xff0c;真的有很多想吐槽的地方&#xff0c;以下就是粤嵌给的一块…

python 修改 网页 navigator 的属性的值

def init_js(page: Page, phone: str) -> Page:#修改 网页 navigator 的属性的值function fakeNav(key, value) {Object.defineProperty(navigator, key, {value: value,writable: false});}js_device_memory ffakeNav("deviceMemory", {gen_android_memory(phon…

Python测试题

目录 题目一&#xff1a;猜数字游戏 题目二&#xff1a;实现一个函数可判断一个数字是否为质数。 题目三&#xff1a;实现一个函数可判断一个数字是否为回文数。 题目四:编写程序实现中美汇率转换。 题目五:球体100米落地弹起运算 题目六&#xff1a;使用python建一个简易…

通达信交易接口API获取数据源码分享

在通达信交易接口下单之前&#xff0c;如何去了解快速获取交易数据呢&#xff1f;要是即使下单了&#xff0c;也不知道如何去查询&#xff0c;然后不管不问&#xff0c;等到需要你去止损了&#xff0c;你才回想起来去操作&#xff0c;这是很容易吃亏的做法的。在股票量化交易过…

符号三角形问题(Java)

符号三角形问题&#xff08;Java&#xff09; 文章目录符号三角形问题&#xff08;Java&#xff09;1、 前置介绍2、算法设计3、程序代码4、算法效率5、参考资料1、 前置介绍 符号三角形定义 如下图所示&#xff0c;符号三角形是由14个“” 号和14个"-"号组成的符号三…

k8s镜像下载不下来?利用 github Action 自己动手一次性解决难题,丰衣足食

docker-image-syncer 无论是在学习k8s还是正式环境部署k8s中,第一步安装k8难倒了各大英雄好汉。原因是k8s 各种组件镜像在谷歌服务器上(k8s.gcr.io)&#xff0c;而我们有墙的存在&#xff0c;所以会经常性的下载失败。解决办法是搭梯子&#xff0c;或者是使用其他镜像源。 本…

有符号变量与无符号变量之间的值的转换

1、有符号变量与无符号变量之间的值的转换 程序例子 涉及到的内容是&#xff1a; 有符号变量和无符号变量之间的转换 溢出&#xff08;如何判断&#xff09; #include<stdio.h> char getchar(int x, int y){char c;unsigned int a x;(x y > 10 ) ? (c 1): (c 2…

Linux——I/O复用(select的用法)

一、I/O复用 定义&#xff1a;I/O 复用使得程序能同时监听多个文件描述符&#xff0c;这对于提高程序的性能至关重要。 网络程序在下列情况下需要使用 I/O 复用技术&#xff1a; ◼ TCP 服务器同时要处理监听套接字和连接套接字。◼ 服务器要同时处理 TCP 请求和 UDP 请求。◼ …

RT-Thread的设备模型

RTT内核对象——设备 RT-Thread有多种内核对象&#xff0c;其中设备device就是其中一种。 内核继承关系图如下&#xff1a; 设备继承关系图如下&#xff1a; I/O 设备模型框架 应用程序通过 I/O 设备管理接口获得正确的设备驱动&#xff0c;然后通过这个设备驱动与底层 I/O 硬…

ARM通用中断控制器GIC之中断处理状态机 Interrupt handling state machine

中断有四种状态&#xff1a;inactive&#xff0c;pending&#xff0c;active 和active and pending。而产生中断的方式有两种&#xff0c;一种是通过写pending寄存器&#xff0c;让中断进入pending状态&#xff0c;可以忽略是否真的有物理中断信号&#xff0c;让Distributor将该…

如何构建myquant量化策略?

对于如何构建myquant量化策略这个问题而言&#xff0c;就是获取量化股票接口的基础数据&#xff0c;然后有了基础数据&#xff0c;才能对数据进行加工处理&#xff0c;构建量化策略&#xff0c;进行量化分析&#xff0c;回测和回溯。myquant量化策略主要是基于python进行量化投…

案例实操 | 利用Lambda函数来进行特征工程,超方便的!!

特征工程对于我们在机器学习的建模当中扮演着至关重要的角色&#xff0c;要是这一环节做得好&#xff0c;模型的准确率以及性能就被大大地被提升&#xff0c;今天小编就通过Python当中的lambda函数来对数据集进行一次特征工程的操作&#xff0c;生成一些有用的有价值的特征出来…

nacos--基础--3.4--集成--spring--spring上下文中的一些关键的特性

nacos–基础–3.4–集成–spring–spring上下文中的一些关键的特性 1、spring上下文中的一些关键的特性 注解驱动依赖注入外部化配置事件驱动 2、注解驱动 2.1、 启用 Nacos EnableNacos 是一个模块驱动的注解EnableNacos 支持 Nacos Spring 的所有功能 服务发现&#xff1…

香港服务器托管带宽怎么选?

香港服务器托管时&#xff0c;带宽的选择非常重要&#xff0c;带宽用量的大小不同&#xff0c;最终的费用也就会不同。用户在香港托管服务器的时候&#xff0c;应该选择多大的带宽才合适呢?应该如何选择香港服务器带宽大小呢? 香港服务器托管带宽怎么选? 其实&#xff0c;大…

5G无线技术基础自学系列 | 无线电波传播模型

素材来源&#xff1a;《5G无线网络规划与优化》 一边学习一边整理内容&#xff0c;并与大家分享&#xff0c;侵权即删&#xff0c;谢谢支持&#xff01; 附上汇总贴&#xff1a;5G无线技术基础自学系列 | 汇总_COCOgsta的博客-CSDN博客 无线电波传播模型用于预测无线电波在各…

JAVA开发(JWTUtil工具类实现token源码赏析)

一、token解决问题的背景&#xff1a; 1、 单点登录&#xff0c; 2、分布式登录状态&#xff0c; 3、在信任期内 避免 重新输入用户名密码登录。 4、跨系统免登陆等。 5、一些接口的请求权限验证。 总结以上token最终都是为了解决在在分布式系统中信任期内 避免 重新输入用户名…

如何选择合适的统计学方法

一般来说&#xff0c;使用哪种统计方法&#xff0c;取决于我们的应用场景、我们的研究目的是什么。 这里面一个麻烦的地方在于&#xff0c;不管你使用哪种统计方法&#xff0c;似乎都可以得出一个’结果’或一个p值&#xff0c;但这个结果有没有意义就两说了。 在我看来&…

【WEB安全】Xstream最新反序列化poc执行报错问题

前言 最近有个需求&#xff0c;用Xstream反序列化打个内存马&#xff0c;从通用性来讲&#xff0c;肯定用1.4.17的洞去打应用范围最广。众所周知&#xff0c;Xstream官方会提供其漏洞的poc。在我实验之下&#xff0c;1.4.17的几个poc只要涉及到任意java代码执行的都会报错&…