基于随机森林Proximity的数据插补(imputation):原理,实现与实验

Random Forest Proximity based missing values imputation: algorithm,implementation and experiments

    • 随机森林和数据插补简介
    • 基于随机森林Proximity的缺失数据插补
      • 原理
      • 算法流程
      • 实现
    • 实验
      • 插补的评价指标
      • 原始Proximity与RF-GAP对比
      • mnist可视化结果
    • 附录

项目主页:randomforest github, gitee
C++ implementation of random forests classification, regression, proximity and variable importance.
推荐阅读:Random Forests C++实现:细节,使用与实验

随机森林和数据插补简介

随机森林(Random Forest, RF)作为一种强大的机器学习算法,已被广泛应用于数据(缺失值)插补(imputation)领域。数据插补是指在处理缺失值时,通过某种方法估计这些缺失值的过程。随机森林在数据插补中的应用主要基于其能够处理分类和回归问题的能力,以及其对缺失数据的鲁棒性。
随机森林通过构建多个决策树并进行集成学习来提高预测的准确性和稳定性。每个决策树都是在不同的样本集合上训练的,这些样本集合是通过对原始数据进行有放回的随机抽样得到的。这种方法的一个关键优势是它能够自动识别和利用数据中的非线性关系,从而在处理具有复杂结构的数据时表现出色。
将随机森林应用于数据插补能获得一些令人满意的特性,比如它能适用于不同类型的缺失数据,能够适应非线性数据,也可以适用于较大的数据集。对于随机森林数据插补算法比较完整的分类介绍,以及对应的使用指导,推荐以下综述型论文。

Tang F, Ishwaran H. Random Forest Missing Data Algorithms. Stat Anal Data Min: The ASA Data Sci Journal. 2017; 10: 363–377.

基于随机森林Proximity的缺失数据插补

原理

基于随机森林Proximity的插补算法不仅适用于连续型(continuous /numerical)特征,也适用于类别型(categorical)或离散型特征。算法核心思想是使用随机森林Proximity来计算缺失样本与非缺失样本之间的相似度,然后通过非缺失样本的加权值来估计缺失值。
现有训练集 { x i , y i } i = 1 N \left \{ \mathbf{x} _i,y_i \right \} _{i=1}^N {xi,yi}i=1N,样本 x i \mathbf{x} _i xi对应类标签或者目标值 y i y_i yi,两个样本之间的相似度为 p ( i , j ) p(i, j) p(i,j)。对于连续型变量,样本某维度的缺失值( x i d = n a n x_i^d=nan xid=nan)通过其他非缺失样本该维度的加权平均计算得到,这里的权值就是样本间的相似度Proximity (the proximity weighted average of non-missing data):

x ˉ i d = ∑ { j : x j d ≠ n a n } p ( i , j ) I ( y i = y j ) x j d ∑ { j : x j d ≠ n a n } p ( i , j ) I ( y i = y j ) \bar{x}_i^d =\frac{ \sum_{\{j:x_j^d \ne nan\} } p\left ( i,j\right ) I\left (y_i=y_j \right )x_j^d}{\sum_{\{j: x_j^d \ne nan\}}p(i,j)I(y_i=y_j)} xˉid={j:xjd=nan}p(i,j)I(yi=yj){j:xjd=nan}p(i,j)I(yi=yj)xjd

对于无序类别型(categorical)变量 x i d ∈ S d x_i^d \in S_d xidSd S d S_d Sd为有限的值集,比如 {0, 1}),缺失值将被插补为该维度上非缺失样本中出现“频度”最高的值,“频度”为累计Proximity (the most frequent non-missing value where frequency is weighted by proximity):
x ˉ i d = arg ⁡ max ⁡   c ∈ S d ∑ { j : x j d = c } p ( i , j ) I ( y i = y j ) \bar{x}_i^d ={ \underset{c\in S_d}{{\arg\max} \, }}\sum_{ \{ j:x_j^d=c\}} p\left ( i,j \right )I\left ( y_i=y_j \right ) xˉid=cSdargmax{j:xjd=c}p(i,j)I(yi=yj)
说明:对于分类问题,上面的公式还考虑了 x i x_i xi x j x_j xj的类别需要相同;对于回归问题,则无需考虑类别。
本文采用RF-GAP (TPAMI2023))算法计算训练集样本之间相似性度量Proximities。RF-GAP利用out-of-bag样本与in-bag样本在叶子节点中的关系计算得到 P ( i , j ) P(i,j) P(i,j),反映了通过随机森林训练得到了数据集几何结构(Geometry)。根据作者的实验,RF-GAP优于原始的随机森林Proximity方法。

算法流程

步骤如下:
1 . 预处理:如果是分类问题,使用训练样本维度的类内中值(连续型变量)或者该维度类内出现频率最高的数值(类别型变量)插补缺失值;如果是回归问题,使用所有样本在该维度上的中值或者出现频率最高的数值插补缺失值。
2. 训练:使用插补后的数据集训练随机森林 F 0 F_0 F0
3. 计算样本的Proximities: P ( i , j ) P(i,j) P(i,j)
4. 使用上一节的公式,更新缺失值
5. 使用更新后的数据集,重新训练随机森林 F t F_t Ft
6. 重复3-5,直到迭代次数达到预设值

以下为代码实现的流程:

具有缺失值的训练集 X = { x 1 , x 2 , . . . . . . , x N } X=\left\{ x_1,x_2,......,x_N \right\} X={x1,x2,......,xN}
以及类标签 Y = { y 1 , y 2 , . . . , y N } Y=\left\{ y_1,y_2,...,y_N \right\} Y={y1,y2,...,yN}
对于每个维度 d = 1...... P d=1......P d=1......P
|  计算该维度上的非缺失样本的中值 m d m_d md(连续型变量),
|  或者出现频率最高的变量值 c d c_d cd(类别型变量)
|  对于每个样本 i = 1...... N i=1......N i=1......N
|     如果 x i d x_i^d xid是缺失值,那么 x i d ← m d (或 c d ) x_i^d \leftarrow m_d(或c_d) xidmd(或cd
通过上述插补方法,获得经过初始填充的训练集 X 0 X_0 X0
训练随机森林 F 0 ← f i t ( X 0 , Y ) F_0 \leftarrow fit(X_0, Y) F0fit(X0,Y)
迭代: t = 1... T t=1...T t=1...T
|  对于每个样本 i = 1...... N i=1......N i=1......N
|  |  使用 F t − 1 F_{t-1} Ft1计算样本 i i i与其他样本之间的Proximities:
|  |     P ( i , j ) P(i,j) P(i,j) , j ∈ 1 , 2 , . . . , N , j\in{1,2,...,N} ,j1,2,...,N
|  |  对于每个维度 d = 1...... P d=1......P d=1......P
|  |  |  如果 x i d x_i^d xid是缺失值,使用上一节公式计算: x ˉ i d \bar{x}_i^d xˉid
|  |  |  更新 X t X_t Xt: x i d ← x ˉ i d x_i^d \leftarrow \bar{x}_i^d xidxˉid
|  |  end of d d d
|  end of i i i
|  训练随机森林 F t ← f i t ( X t , Y ) F_t \leftarrow fit(X_t, Y) Ftfit(Xt,Y)
end of t t t

实现

上述Random-Forest-Proximity-based数据插补方法计划更新到“项目randomforest”。
算法复杂度为 O ( P ∗ N 2 ) O(P*N^2) O(PN2),其中 P P P N N N分别为维数和样本数。另外,当样本数较大时,Proximities矩阵 ( N × N N\times N N×N) 无法直接计算。如对于mnist数据集(样本数60000),它的完整Proximities矩阵需要占用26G以上 ( 60 k × 60 k × 8 60k\times60k\times8 60k×60k×8)内存。此外,由于RF-GAP的相似度矩阵 P ( i , j ) ≠ P ( j , i ) P(i,j)\ne P(j,i) P(i,j)=P(j,i),因此也没有必要将完整的Proximities矩阵计算出,每次计算样本 i i i与其它样本的相似度即可。
以下为主干代码:

float** MissingValuesImputaion(float** data, bool* is_categorical, const float** data_orig, int* label, RandomCForests_info RFinfo, PROXIMITY_TYPE prox_type, int max_iteration, bool verbose, int random_state, int jobs)
{
	int rv = chekcDataSetLabel(label, RFinfo.datainfo.samples_num, RFinfo.datainfo.classes_num);
	if (rv < 0)
	{
		std::cout << ">>>[MissingValuesImputaion] labels are not properly set." << std::endl;
		return NULL;
	}

	const int samples_num = RFinfo.datainfo.samples_num;
	const int var_num = RFinfo.datainfo.variables_num;
	const int class_num = RFinfo.datainfo.classes_num;


	bool todel = false;
	if (is_categorical == NULL)
	{
		todel = true;
		is_categorical = new bool[RFinfo.datainfo.variables_num];
		memset(is_categorical, 0, sizeof(bool) * RFinfo.datainfo.variables_num);
	}

	unsigned char* rowmask = new unsigned char[samples_num];
	memset(rowmask, 0, sizeof(unsigned char) * samples_num);

	// generate mask of the sample row and matrix
	unsigned char** nanmask = new unsigned char* [samples_num];
	for (int n = 0; n < samples_num; n++)
	{
		nanmask[n] = new unsigned char[var_num];
		memset(nanmask[n], 0, sizeof(unsigned char) * var_num);
		for (int j = 0; j < var_num; j++)
		{
			if (std::isnan(data[n][j]))
			{
				nanmask[n][j] = 1;
				rowmask[n] = 1;
			}
		}
	}

	float** data_imp = clone_data(data, samples_num, var_num);
	bool** bzero = MedianFill(data, is_categorical, label, RFinfo, data_imp/*OUT*/);
	float** data_tmp = clone_data(data_imp, samples_num, var_num);

	int method_nrmse = 0;  // 0: RMSE; 1: NRMSE
	float *var_true = NULL; // variance of the ture(original) values along a variable(feature)
	float* mean_true = NULL;
	int* count_var = NULL; // missing values of a variable(feature)
	float* nrmse = NULL; //  the normalized root mean squared error (NRMSE)
	if (data_orig)
	{
		var_true = new float[var_num * 3];
		mean_true = var_true + var_num;
		nrmse = var_true + var_num * 2;
		memset(var_true, 0, sizeof(float) * var_num * 3);

		count_var = new int[var_num];
		memset(count_var, 0, sizeof(int) * var_num);

		PreparePrecisionCalculation(samples_num, var_num, data_orig, nanmask, count_var, mean_true, var_true);

		PrintPrecision(samples_num, var_num, data_imp, data_orig, nanmask, is_categorical, count_var, var_true, method_nrmse);
	}

	if (jobs <= 0)
		jobs = 1;

	if (jobs > 1)
	{
		std::cout << "max_threads: " << omp_get_max_threads() << std::endl;
		jobs = jobs > omp_get_max_threads() ? omp_get_max_threads() : jobs;
		omp_set_num_threads(jobs);
		std::cout << "jobs: " << jobs << std::endl;
	}

	for (int it = 0; it < max_iteration; it++)
	{
		LoquatCForest* forest = NULL;

		if (jobs > 1)
			TrainRandomForestClassifierOMP(data_tmp, label, RFinfo, forest, random_state, jobs, verbose ? 50 : 0);
		else
			TrainRandomForestClassifier(data_tmp, label, RFinfo, forest, random_state, verbose ? 50 : 0);

		LoquatCTreeNode*** leafMatrix = NULL;
		if (prox_type == PROXIMITY_TYPE::PROX_ORIGINAL)
		{
			leafMatrix = createLeafNodeMatrix(forest, data_tmp);
		}

		timeIt(1);
#pragma omp parallel for num_threads(jobs)
		for (int n = 0; n < samples_num; n++)
		{
			if (!rowmask[n])
				continue;

			float* proximity = NULL;
			int rv = 1;
			switch (prox_type)
			{
			case PROXIMITY_TYPE::PROX_ORIGINAL:
				rv = ClassificationForestOrigProximity2(forest, data_tmp[n], leafMatrix, proximity);
				break;
			case PROXIMITY_TYPE::PROX_GEO_ACC:
			default:
				rv = ClassificationForestGAPProximity(forest, data_tmp, n, proximity);
			}
			
			if (rv < 0)
			{
				delete[] proximity;
				continue;
			}

			const int lb = label[n];
			for (int v = 0; v < var_num; v++)
			{
				if (!nanmask[n][v])
					continue;

				bool use_label = !bzero[v][lb];
				if (!is_categorical[v])
				{
					float s = 0.f, estimated = 0.f;

					for (int i = 0; i < samples_num; i++)
					{
						if (!nanmask[i][v] && (!use_label || label[i] == lb))
						{
							s += proximity[i];
							estimated += data_tmp[i][v] * proximity[i];
						}
					}

					if (s > 1e-10f)
					{
						estimated = estimated / s;
						data_imp[n][v] = estimated;
					}

				}
				else
				{
					int k;
					std::unordered_map<int, float> categroy_prox;
					for (int i = 0; i < samples_num; i++)
					{
						if (!nanmask[i][v] && (!use_label || label[i] == lb))
						{
							k = int(data_tmp[i][v]);
							if (categroy_prox.end() == categroy_prox.find(k))
								categroy_prox.emplace(k, proximity[i]);
							else
								categroy_prox[k] += proximity[i];
						}
					}

					int k_max_prox = -1;
					float max_prox = -FLT_MAX;
					for (auto it = categroy_prox.begin(); it != categroy_prox.end(); ++it)
					{
						if (it->second > max_prox)
						{
							max_prox = it->second;
							k_max_prox = it->first;
						}
					}
					data_imp[n][v] = k_max_prox;
				}

			}

			delete[] proximity;

			if (n > 0 && n % 5000 == 0)
				std::cout << "samples " << n << " imputated." << std::endl;
		}

		std::cout << "proximities: " << timeIt(0) << std::endl;

		ReleaseClassificationForest(&forest);

		if (prox_type == PROXIMITY_TYPE::PROX_ORIGINAL)
		{
			for (int n = 0; n < samples_num; n++)
				delete[] leafMatrix[n];
			delete[] leafMatrix;
		}

		if (data_orig)
		{
			PrintPrecision(samples_num, var_num, data_imp, data_orig, nanmask, is_categorical, count_var, var_true, method_nrmse);
		}


		for (int n = 0; n < samples_num; n++)
			memcpy(data_tmp[n], data_imp[n], sizeof(float) * var_num);
	}


	for (int n = 0; n < samples_num; n++)
	{
		delete[] nanmask[n];
		delete[] data_tmp[n];
	}

	delete[] nanmask;
	delete[] data_tmp;
	delete[] rowmask;

	delete[] var_true;
	delete[] count_var;

	for (int v = 0; v < var_num; v++)
		delete[] bzero[v];
	delete[] bzero;

	if (todel)
		delete[] is_categorical;

	return data_imp;
}

数据插补没有实际动手写代码前感觉不那么复杂,但实际上需要考虑很多细节,比如要考虑是否某个维度上某个类别对应的数据都缺失的情况。关于归一化(标准化)预处理,如果实际工作中没有真实数据集做参考(结果对比),完全没必要对输入数据集在各自维度上进行标准化。

实验

插补的评价指标

采用NRMSE (normalized root mean squared error)指标评价算法插补数据的准确性,这个指标被其他数据插补的论文广泛使用。

N R M S E = 1 ∣ N ∣ ∑ j ∈ N m e a n ( ( x j − x ˉ j ) 2 ) v a r ( x j ) NRMSE=\frac{1}{| \mathcal{N}|} \sum_{j \in \mathcal{N}} {\sqrt{\frac{mean\left ( \left ( \mathbf{x}^j-\bar{\mathbf{x}} ^j \right )^2\right ) }{var\left ( \mathbf{x} ^j\right ) } } } NRMSE=N1jNvar(xj)mean((xjxˉj)2)

其中 , N = { j : ∃ x i j   i s    m i s s i n g    v a l u e s } 其中, \mathcal{N} = \left \{ j : \exists x_i ^j \, is \,\,missing \,\,values \right \} 其中,N={j:xijismissingvalues}

x j \mathbf{x}^j xj x ˉ j \bar {\mathbf{x}}^j xˉj分别表示训练集维度 j j j的真实数据和插补后的数据, 仅针对缺失数据计算对应的 m e a n mean mean v a r var var

“where X t r u e X^{true} Xtrue and X i m p X^{imp} Ximp are the true and imputed data matrix, respectively, and the mean and variance are computed only over the missing values.” — —
Shangzhi Hong,Henry S. Lynn. Accuracy of random-forest-basedimputation of missing data in the presenceof non-normality, non-linearity, and interaction. BMC Medical Research Methodology, 2020.

由于评价指标NRMSE要计算方差,可能存在不确定性(比如,某个维度缺失值较少方差统计不准确或者接近于0)。如果每个维度的物理属性是一致的情况下(比如字符识别数据集mnist),或者数据的每个维度进行了归一化,也可以使用RMSE指标来评价插补的效果:
R M S E = 1 ∣ N ∣ ∑ j ∈ N m e a n ( ( x j − x ˉ j ) 2 ) RMSE=\frac{1}{|\mathcal{N}|} \sum_{j \in \mathcal{N}} \sqrt {mean\left ( \left ( \mathbf{x}^j-\bar{\mathbf{x}}^j \right )^2\right ) } RMSE=N1jNmean((xjxˉj)2)

原始Proximity与RF-GAP对比

在若干数据集上验证上述算法的数据插补效果,并与原始随机森林数据插补方法进行对比。缺失数据的模拟方法是完全随机的(MCAR, Missing Completely At Random),即以一定概率(对于所有样本和维度是一致的)将原数据值替换为缺失值(nan)。
本文缺失值插补算法的核心是Proximities的计算,将RF-GAP 与原始随机森林Proximity方法的插补方法进行对比。迭代2次,运行3次取平均数。原始Proximities计算方法中不区分袋内(in-bag)袋外(oob)数据,直接统计样本落入到相同叶子节点的概率 p ( i , j ) = 1 T ∑ t I ( l e a f i t = l e a f j t ) p(i,j)=\frac{1}{T}\sum_tI(leaf_i^t=leaf_j^t) p(i,j)=T1tI(leafit=leafjt)

数据集 (样本数/特征维数/类别数或输出维度)随机森林参数评价方法缺失百分比Proximity方法
Original  GAP
pendigits (7494/16/10)[200,4, 40, 5]NRMSE 10%0.5287 0.4359
20%0.5637 0.4553
30%0.6002 0.4791
mnist(60000/780/10)[200,27,40,5]RMSE 10%41.32 36.70
20%42.68 38.21
30%43.449839.6647
Sensorless_drive_diagnosis1
(58509/48/11)
[200,6,40,5]RMSE10%0.0436 0.0371
20%0.0461 0.0389
30%0.0486 0.0417
MAGIC_Gamma_Telescope
(19020/10/2)
[200,3,40,5]NRMSE10%0.7707 0.6998
20%0.8552 0.7372
30%0.9424 0.7992
ionosphere(351,34,2)[200,6,40,5]NRMSE10%0.9192 0.8479
20%0.9278 0.8651
30%0.9292 0.8676
spambase(4601,57,2)[200,7,40,5]RMSE10%0.0492 0.0476
20%0.0504 0.0494
30%0.0506 0.0501
letter-recognition(20000,16,26)[200,4, 40, 5]NRMSE10%0.5959 0.4808
20%0.6463 0.5203
30%0.6981 0.5658
Concrete_Compressive_Strength
(1030,8,1)
[200,3,40,5]NRMSE10%0.6001 0.6388
20%0.6695 0.7019
30%0.7035 0.7491
airfoil_self_noise(1503,5,1)[200,2,40,5]NRMSE10%0.6503 0.6946
20%0.7136 0.7532
30%0.7647 0.7973
  1. Sensorless_drive_diagnosis数据集使用NRMSE存在问题,因为其第40维特征的数值分布[-0.90235~3670.8]极其不均,极个别值偏离度很大,使用NRMSE在该维度上结果超过4,并不能反映插补效果。因此将每个特征(维度)均压缩到[0,1], 使用RMSE。

以上表中,“Original”为原始随机森林Proximity,“GAP”为TPAMI2023方法,数值为原始样本与插补样本间的平均NRMSE或者RMSE,数值越小表示效果越好。此结果与论文TPAMI2023相符。

mnist可视化结果

这里使用mnist数据集,样本数为60000,特征维数780,样本是手写数字的图像,数值在0~255之间(非二值化图像)。模拟缺失数据的方法是,对于图像中非零元素按一定概率将原值替换为缺失值(nan)。这样做的目的是为了验证上述算法对于非完全随机(Missing at Random, MAR)情况下缺失值插补的效果。另一方面,这样也更利于可视化展现插补的效果。
以下随机选取了若干样本插补的效果。第一列为原始样本,第二列是具有缺失值的样本(样本上的黑点就是人为制造的缺失值),第三列是使用“中值”插补预处理的结果,第四列是使用Proximities插补后的结果。
可以观察到,虽然与原样本图像相比还有一定差距,但是Proximities插补后“缺失”情况有明显改善。如果增加一些先验信息,插补效果还能进一步提升。

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

附录

使用我实现的randomforest算法算法进行缺失值插补的代码如下:

// 读取原始数据集,无缺失值
char filename[500] = "E:\\randomforest\\DataSet\\Classification\\pendigits.tra";
float** data = NULL;
int* label = NULL;
Dataset_info_C datainfo;
InitalClassificationDataMatrixFormFile2(filename, data/*OUT*/, label/*OUT*/, datainfo/*OUT*/);

RandomCForests_info rfinfo;
rfinfo.datainfo = datainfo;
rfinfo.maxdepth = 40;
rfinfo.ntrees = 200;
rfinfo.mvariables = (int)sqrtf(datainfo.variables_num);
rfinfo.minsamplessplit = 5;
rfinfo.randomness = 1;

float** data_out = NULL;
int random_state = 40;

// 模拟制造缺失值
int nan_num = 0, sample_with_mv = 0;
float** data_mv = clone_data(data, datainfo.samples_num, datainfo.variables_num); 
for (int n = 0; n < rfinfo.datainfo.samples_num; n++)
{
	int sample_have_mv = false;
	for (int v = 0; v < rfinfo.datainfo.variables_num; v++)
	{
	    // 缺失比例
		if (rand_freebsd() % 100 < 10 /*&& data[n][v]>0*/ /*mnist!!!*/ )
		{
			sample_have_mv = true;
			data_mv[n][v] = std::nanf("");
			nan_num++;
		}
	}
	if (sample_have_mv)
		sample_with_mv++;
}
	
cout << "sample with missing values: " << sample_with_mv << endl;
cout << "total missing values: " << nan_num << endl;

const float** data_orig = (const float **)data;
// 标志哪些特征是categorical,以下假设全部特征都不是categorical
bool* is_cate = new bool[rfinfo.datainfo.variables_num];
memset(is_cate, 0, sizeof(bool) * rfinfo.datainfo.variables_num);

// 缺失值插补,返回插补后数据集
data_out = MissingValuesImputaion(data_mv, is_cate, data_orig, label, rfinfo, PROX_GEO_ACC, 2/*迭代次数*/, true, random_state, 8/*线程数*/);
	
for (int n = 0; n < datainfo.samples_num; n++)
{
	delete[]data_mv[n];
	delete[]data[n];
	delete[]data_out[n];
}
delete[]data;
delete[] data_mv;
delete[] data_out;
delete[] label;
delete[] is_cate;
return 0;

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【云原生】用 Helm 来简化 K8s 应用管理

用 Helm 来简化 K8s 应用管理 1.诞生背景2.主要功能3.相关概念4.工作原理5.架构演变6.Helm 常用命令7.推荐仓库8.Charts8.1 目录结构8.2 构建一个无状态应用模版 charts Helm 对于 Kubernetes 来说就相当于 Yum 对于 Centos 来说&#xff0c;如果没有 Yum 的话&#xff0c;我们…

LiveGBS流媒体平台GB/T28181用户手册-国标级联:添加上级平台、选择通道、推送通道级联会话、搜索、删除

LiveGBS流媒体平台GB/T28181用户手册-国标级联:添加上级平台、选择通道、推送通道级联会话、搜索、删除 1、国标级联1.1、添加上级平台1.2、注册状态1.3、选择通道1.4、推送通道1.5、级联会话1.6、搜索1.7、删除 2、搭建GB28181视频直播平台 1、国标级联 1.1、添加上级平台 点…

四川音盛佳云电子商务有限公司可靠吗?怎么样?

在数字经济的浪潮中&#xff0c;抖音电商以其独特的魅力逐渐崭露头角&#xff0c;成为电商领域的一股新势力。而四川音盛佳云电子商务有限公司&#xff0c;正是这股新势力中的佼佼者&#xff0c;以其专业的服务和创新的理念&#xff0c;引领着抖音电商的发展潮流。 四川音盛佳…

python Z-score标准化

python Z-score标准化 Zscore标准化sklearn库实现Z-score标准化手动实现Z-score标准化 Zscore标准化 Z-score标准化&#xff08;也称为标准差标准化&#xff09;是一种常见的数据标准化方法&#xff0c;它将数据集中的每个特征的值转换为一个新的尺度&#xff0c;使得转化后的…

Java-数组内存解析

文章目录 1.内存的主要结构&#xff1a;栈、堆2.一维数组的内存解析3.二维数组的内存解析 1.内存的主要结构&#xff1a;栈、堆 2.一维数组的内存解析 举例1&#xff1a;基本使用 举例2&#xff1a;两个变量指向一个数组 3.二维数组的内存解析 举例1&#xff1a; 举例2&am…

[Linux系统编程] 静态库与动态库

一.库的概念 库是写好的现有的&#xff0c;成熟的&#xff0c;可以复用的代码。 本质上来说库是一种可执行代码的二进制形式&#xff0c;可以被操作系统载入内存执行。库有两种&#xff1a;静态库&#xff08;.a、.lib&#xff09;和动态库&#xff08;.so、.dll&#xff09;。…

nvidia origin nx 刷机JETPACK 6

条件&#xff1a; 1.sdkmanager 链接&#xff1a;https://pan.baidu.com/s/1pmeT7_vKF_NXvP8xEhCelw?pwd8q1e 提取码&#xff1a;8q1e 2.ubuntu 20或者ubuntu22 3.nvidia orin nx Nvidia Jetson Orin NX&#xff08;一&#xff09;开始刷机_jetson nx刷机-CSDN博客 …

CatDDoS僵尸网络与DNSBomb攻击:DDOS攻击最新变种

在近期的网络安全领域&#xff0c;两大严峻挑战浮出水面&#xff0c;为中国乃至全球的网络防护体系敲响了警钟。中国安全厂商奇安信的X实验室团队揭露了一波名为CatDDoS的恶意软件攻击浪潮&#xff0c;与此同时&#xff0c;一种创新且隐蔽的拒绝服务攻击技术——DNSBomb&#x…

操作系统真象还原:完善MBR

第3章-完善MBR 这是一个网站有所有小节的代码实现&#xff0c;同时也包含了Bochs等文件 编译器给程序中各符号&#xff08;变量名或函数名等&#xff09;分配的地址&#xff0c;就是各符号相对于文件开头的偏移量 。 section 称为节&#xff0c;在有的编译器中&#xff0c;同…

【注册表删除】Navicat Premium 16的试用期已过期的解决方案

文章目录 【问题】【解决方式】 【问题】 使用Navicat Premium 16提示试用期已过期&#xff0c;如下图所示情况&#xff1a; 【解决方式】 1、关闭Navicat Premium 16 确保Navicat Premium 16处于关闭状态。 2、Win R&#xff0c;输入 regedit 回车 3、删除Data文件夹 在注…

硬盘监控,保障硬盘性能

硬盘驱动器是个人计算机和服务器中用于存储数字数据的硬件部件&#xff0c;硬盘突然故障可能导致永久数据丢失&#xff0c;大多数硬盘驱动器使用自我监控、分析和报告技术&#xff08;SMART&#xff09;来跟踪各种性能指标并分析其自身的运行状况。然而&#xff0c;并不是所有的…

史上最全排序算法整理(2)

本篇文章我们将接着上篇继续介绍常见的排序算法&#xff0c;有需要的小伙伴可以移步史上最全排序算法整理&#xff08;1&#xff09;查看相关内容哦 1.冒泡排序 1.1基本思想 在待排序的一组数中&#xff0c;将相邻的两个数进行比较&#xff0c;若前面的数比后面的数大就交换两…

FFMPEG+ANativeWinodow渲染播放视频

前言 学习音视频开发&#xff0c;入门基本都得学FFMPEG&#xff0c;按照目前互联网上流传的学习路线&#xff0c;FFMPEGANativeWinodow渲染播放视频属于是第一关卡的Boss&#xff0c;简单但是关键。这几天写了个简单的demo&#xff0c;可以比较稳定进行渲染播放&#xff0c;便…

软件系统测试的类型和方法介绍

测试是软件开发过程中至关重要的一环&#xff0c;负责验证和确认软件系统是否符合预期的需求&#xff0c;并帮助开发团队消除潜在的缺陷。系统测试作为软件测试中不可缺少的过程&#xff0c;是根据预先制定的测试计划和测试用例&#xff0c;以检查软件系统功能、性能、安全性和…

LED显示屏模组七大参数

LED模组是LED显示屏的核心组件&#xff0c;它包含LED线路板和外壳&#xff0c;将LED灯珠按照特定规则排列并封装&#xff0c;通常还会进行防水处理。随着LED显示屏行业的发展及其广泛应用&#xff0c;LED模组的功能和作用变得愈加重要。那么&#xff0c;LED模组的七大参数是什么…

开抖店必须要办理营业执照吗?不用营业执照开店的个人店能用吗?

大家好&#xff0c;我是电商花花。 可能大家都发现了&#xff0c;抖音小店个人店不用营业执照&#xff0c;只凭借身份证就能开店。 但是这个个人店花花并不建议大家去开&#xff0c;虽然说用用身份证也能开店&#xff0c;有效的帮我们减少了开店的成本&#xff0c;但是个人店…

【RLHF个人笔记】RLHF:Reinforcement Learning from Human Feedback具体过程

【RLHF个人笔记】RLHF:Reinforcement Learning from Human Feedback具体过程 RLHF训练的三个步骤步骤1&#xff1a;收集数据与有监督训练策略步骤2&#xff1a;收集数据训练奖励模型步骤3&#xff1a;结合奖励模型利用强化学习算法如PPO算法来优化策略 参考内容 RLHF训练的三个…

jeecgboot 同一账号只允许一个人登录

1.需求分析 jeecgboot 框架要实现同一个账号只允许一个人登录&#xff0c;就跟游戏账号类似&#xff0c;“我登录了就把你踢下去&#xff0c;你登录了就把我踢下去”&#xff1b;jwt 原理是生成 token 后一段时间内登录都有效&#xff0c;jeecgboot 中 jwt 和 redis 联合使用后…

易备数据备份软件:快速恢复 VMware ESXi 虚拟机

易备数据备份软件为 VMware ESXi 虚拟机提供完整的保护和备份功能。软件同时支持从 ESXi 或 vCenter 虚拟机的增量和差异备份中进行自动恢复。支持精细化的恢复&#xff0c;可将虚拟机恢复到某个特定的日期。 通过易备数据备份软件&#xff0c;可以实现虚拟机的异机恢复&#…

深入理解JVM:内存结构、垃圾收集与性能调优

目录 JDK、JRE、JVM关系? 启动程序如何查看加载了哪些类&#xff0c;以及加载顺序? class字节码文件10个主要组成部分? JVM结构 画一下JVM内存结构图 程序计数器 Java虚拟机栈 本地方法栈 Java堆 方法区 运行时常量池? 什么时候抛出StackOverflowError? 例如&…