Python数据分析案例70——基于神经网络的时间序列预测(滞后性的效果,预测中存在的问题)
背景
这篇文章可以说是基于 现代的一些神经网络的方法去做时间序列预测的一个介绍科普,也可以说是一个各种模型对比的案例,但也会谈一谈自己做了这么久关于神经网络的时间序列预测的论文,其中一些常见的模式及它们存在的问题以及效果,还有在实际生产中去运用究竟会有什么问题?
时间序列预测的几种模式
首先我们要知道一个滑动窗口的概念,时间序列预测无非就是用之前的数据去预测未来的数据。与传统的机器学习的二维表格不同,时间序列最简单的模式就是单变量单步预测。即我们要预测一个变量,我们就用这个变量前几期的数据去预测未来下一期的数据,例如我们的滑动窗口大小为10,也就是说我们用这个变量 'Xt-10到Xt' 的数据,去预测' Xt+1' 的取值(这里就不写latex公式了反正应该也挺通俗易懂的)。也就是说,在单变量单步预测模型中,你的一个X向量就是 'Xt-10到Xt' 的数据 ,y标量就是 ' Xt+1' 的取值。然后我们可以不停的滑动这个窗口,得到很多组X跟y,就可以去训练模型了。(注意这里的X是三维数据,形状为(n,t,p), n为样本量,t是时间步长,也就是滑动窗口大小,p是特征数量,这里单变量就是1,y形状为(n,1)的向量 )
现代神经网络基本都是这样做的,所以这导致了一个问题,我们要预测' Xt+2' 就没有办法了,因为我们不知道' Xt+1' 的真实值。有的人说可以吧 'Xt+1' 的预测值放进去继续预测,确实,传统模型都是这样做的,但是这样势必又会导致另外一个问题,即误差的累计,因为 'Xt+1' 的预测值不是真实值,所以有误差,预测出来的'Xt+2'就是在有误差的数据上预测出来的,那效果肯定会更差,并且时间步长越久,误差越夸张。 根据经验,这种方法预测出来到三步,五步以后,基本都没法看,和真实值差距是天涯海角。(传统的ARIMA都是这样做的,所以基本上很多人用这个模型都会发现他们预测出来的数据就是一条直线..........)
所以这就衍生出了一个问题,那我们要做多步预测该怎么办?这个也是我们要谈到的神经网络的第二种预测模式,即单变量多步预测。既然我们因为不知道'Xt+1'所以没办法预测'Xt+2',那我们干脆直接用 'Xt-10到Xt' 的数据,去预测出来 'Xt+1 到 Xt+5'(假设我们要预测5步),很多计量经济学和传统统计学的同学要炸毛了,因为在传统的统计学模式基本看不到这种y取值是多个的情况,在无数的传统统计学和计量的模型以及一些基础的机器学习模型,无论是分类还是回归问题,我们的y都是一个标量,都是一个具体的取值,一个数字,而不是一个向量。向量也能作为y? 在神经网络里面是可以的,毕竟深度学习连图片,连音频,连文本, 视频都可以作为y,一个向量作为y是很正常的事情,大部分同学做模型的时候连数据的形状都没弄清楚,其实这是一个很重要的认知,你一定要认清楚你的X跟y是什么形状,你才能去用对应的模型。
回到我们的单变量多步预测模型上来,我们的X还是之前的X,形状为(n,t,p),但是我们的y却不再是一个向量,而是一个矩阵形状为(n,T) ,T为你要预测的时间的步长,多步神经网络有2种方法去训练,即直接要预测几步, 你的输出层就用几个神经元:
outputs = Dense(5, activation='linear')(pooled_output) # 输出形状: (n, 5)
pooled_output是上一层经过池化 或者是循环神经网络最后一刻的状态,有可能是经过展开的mlp,反正就是将输入的X从3维变为2维度之后的情况,输入的pooled_output是二维数据,这样输出的就是一个(n,5)的向量,即n个样本,每个样本都预测了5步。这种方法直观,我目前基本上做的多步预测都是用这个方法。
也可以使用另外一种方法训练:使用 TimeDistributed
lstm_output = LSTM(16, return_sequences=True)(inputs) # 输出形状: (n, 10, 16)
# 使用 TimeDistributed
outputs = TimeDistributed(Dense(1, activation='linear'))(lstm_output) # 输出形状: (n, 10, 1)
可以看到,这里的滑动窗口的时间步长和预测的步长就得是一样的。输入的lstm_output还是三维进去,三维出来,然后直接直接转为二维y矩阵了。(y就一个特征所以可以直接reshape为(n,10))。所以这种方法不能灵活的控制你的输入滑动窗口的步长跟你要预测的时间步长,必须一样。至于精准度和上面的方法比起来,我也没试过哪一种好。
讲完了上面的单变量的单步和多步预测,下面另外两种模式也更好理解,也无非就是X特征变多了,是多变量的单步跟多变量的多步预测。
其实思路和训练代码上没有太多差异,X形状还是三维,形状为(n,t,p), 这里的p就不是1了,是2以上。单步预测的y还是(n,1),多步还是(n,T), 只是在构建我们的训练集和测试集的时候,需要注意一下这个多维度的时候,构建X和y去对数据运用切片索引的问题。
按道理来说,以前基于树模型的二维表格的机器学习都是变量越多越好,但是在如今的这个循环神经网络里面并不是特征p越多越好。时间序列很看你上一时刻的这些变量的情况,有的时间序列特征噪音会特别多,而且会突变(例如之前的可能取值都是1,后面突然一下变成100),会对我们要预测的变量造成严重的干扰,这个可能也得进行一定的选择。
还有一些别的模式,例如用'Xt-10到Xt' 去预测 'Xt+8' 或者是 'Xt+9', 这种美言自称是多步预测的模式,我就不多说了,这本质也是单步预测..............都是一个换汤不换药的概念。(反正在自己的论文里面咋吹都是合理的)
神经网络预测时间序列预测的一些问题
常见的时间序列预测的模式都讲完了,下面再来讲一下。这些模式会有哪些问题?
首先就是很多新手刚开始做出一个模型,预测值和真实值一对比就会有一个:滞后性的困惑
所谓滞后性的困惑就是如上图一样,我们的预测值看起来就像真实值往后挪了一个时间单位。可能我这个图有点密集,画的太小看不清楚,但整体而言,滞后性的困惑 就是我们预测出来的'Xt+1'的值好像就是上一时刻Xt的值,看起来我们的预测无非就是把真实值进行了滞后一期罢了。
很多人不懂为什么,我刚开始学的时候也有这个困惑,但是后来做多了也就没管了,反正大家都这样干,我也就这样干吧。但现在我大概明白了里面的这个原理,和大家讲一讲。
首先一些正常的时间序列,例如股票价格,空气质量,人体的血糖浓度......都是具有强烈的自相关的序列数据,这点是不可否认的,即我们的'Xt+1'跟'Xt'肯定是具有高度的相关性系数,并且是线性相关。就例如股价你无论再怎么变,你肯定也是昨天价格的±10%的区间,不可能离昨天的值差距太远太离谱。所以就造成了这种今天的价格跟昨天的价格是强烈的线性正相关的关系。
我们都知道随机过程中经典的醉汉问题,即一个醉汉如果在开始坐标为(0,0)的二维平面上开始进行随机游走,每1秒钟走一步,那么走了十几个小时或者是一天之后,我们在哪里找到这个醉汉的概率最大?
答案是原点,就是(0,0)的位置,因为是随机游走,在随机游走中,醉汉的每一步移动是独立的,且在每个方向上的移动是随机的。假设醉汉在二维平面上每秒钟移动一步,每一步在x轴和y轴上的移动是独立的,且在每个方向上(左、右、上、下)的概率相等。他的动向的分布肯定是符合正态的,符合均值为零的。
所以再回到我们的时间序列预测问题。我们要预测下一时间的Xt+1时间的取值,也就是去找可能概率最大的取值,其可能性在哪里呢?那么就在上 Xt 上,也就是Xt的取值是作为'Xt+1'的概率最大,所以模型们都很聪明——他们基本都学到了概率最大的情况,也就是:直接把昨天的值稍微修改那么一点点,作为今天的预测值就好了,这也是为什么我们用循环神经网络做这种单步预测的时候,发现这种有滞后性的困惑的问题。本质就是时间序列的跟上一时刻的强烈的自相关带来的概率化最优的问题。
当然实际上模型肯定不会直接用上一个的取值完全相同作为下一时间的预测值,因为有些时间序列的动向变化肯定也不会是完全的概率相同的随机过程。他们神经网络模型肯定还是会根据数据的一些模式,例如季节性,波动性,趋势性学到一点点的修改。但是这种修改到底是好还是坏呢?我们肯定是用一些误差指标来进行衡量。然后进行一个对比。
这就引出了第二个问题,即 神经网络预测出来的都是类似滞后一期的数据。那我能不能构建一个基准模型——即MA(1)模型,直接用Xt 的值作为'Xt+1'的预测值,然后在整个样本上计算误差评价指标和神经网络预测出来的预测值进行一个对比,我们来看看效果到底行不行。
为什么叫MA(1)模型,MA大家都知道是移动平均模型,即用前n天的真实值平均一下作为每天的预测值。如果我们极端一点,取n等于1, 那么就是用前一天的平均值作为下一天的预测值,也就是说直接用今天的真实值作为下一天的预测值,我们就可以对比——我们花费了无数时间精力学习到构建出来的循环神经网络模型(整了一堆乱七八糟的卷积门控注意力机制transformer层)和我们最简单的MA(1)模型对比到底,到底能够'强多少'?
这就是今天这个案例的目的。
数据介绍
本次用的数据都是之前的案例常用的一些时间序列,我懒得找新的了,就主要是这5个:
每个数据都是两列,一列时间,一列是它的取值,当然它们的时间频率不一样,但是无所谓,神经网络也不管你是日度还是月度还是季度的,还是甚至是秒级的,反正都是一样训练。
本次下面演示的就用石油价格这个序列进行划分训练集和测试集,构建3维的数据张量,进行单变量单步预测。
对比如下的神经网络模型:
["Transformer", "CNN+BiLSTM", "BiGRU-Attention","BiLSTM-Attention", "BiGRU", "BiLSTM", "TCN", "GRU", "CNN", "LSTM","RNN","MLP", ]
在目前大量外行还在用lstm这种模型发论文的时候,我上面就随便这几个模型都可以写一篇普通期刊的论文,再缝合一点模态分解,优化算法或者损失函数都可以发SCI了。并且他们的构建很简单,我全部都统一化和模块化了。
当然,需要本次演示的数据和全部代码文件的同学还是可以参考:神经网络时间序列
代码实现
导入包,我们用keras框架,默认TensorFlow后端。3.0以上可以用pytorch作为后端,API接口类似。
import numpy as np
import pandas as pd
import matplotlib.pyplot as plt
import seaborn as sns
import os
import time
from datetime import datetime
import random as rn
import scipy.special as sc_special
plt.rcParams ['font.sans-serif'] ='SimHei' #显示中文
plt.rcParams ['axes.unicode_minus']=False #显示负号from sklearn.preprocessing import MinMaxScaler
from sklearn.metrics import mean_absolute_error
from sklearn.metrics import mean_squared_error,r2_scoreimport tensorflow as tf
import keras
import keras.backend as K
from keras.models import Model, Sequential
from keras.layers import Dense,Input, Dropout, Flatten,MaxPooling1D,Conv1D,SimpleRNN,LSTM,GRU,GlobalMaxPooling1D,Layer
from keras.layers import BatchNormalization,GlobalAveragePooling1D,MultiHeadAttention,AveragePooling1D,Bidirectional,LayerNormalization
from keras.callbacks import EarlyStopping
这个框架的代码做了太多次了,以前的文章也有非常多,所以说我也不过多的写解释了,下面就简单罗列一下这个过程。都是为了我们要展示一下最终的结果对比罢了。
读取数据
data0=pd.read_excel('时间序列测试数据.xlsx',parse_dates=['date'],sheet_name=1).set_index('date').ffill()
data0.head()
sheet_name=1,因为我的石油价格是装在第二个sheet里面,所以我们用等于一,然后把data设置为时间索引。最后的.ffill 是为了防止数据的缺失值,就用前一个值进行填充。
展示数据折线图
data0.plot(figsize=(12,3))
构建训练集和测试集
单变量单步模型,构建X和y的函数:
def build_sequences(text, window_size=24):#text:list of capacityx, y = [],[]for i in range(len(text) - window_size):sequence = text[i:i+window_size]target = text[i+window_size]x.append(sequence)y.append(target)return np.array(x), np.array(y)def get_traintest(data,train_ratio=0.8,window_size=24):train_size=int(len(data0)*train_ratio)train=data[:train_size]test=data[train_size-window_size:]X_train,y_train=build_sequences(train,window_size=window_size)X_test,y_test=build_sequences(test,window_size=window_size)return X_train,y_train,X_test,y_test
划分训练集和测试集,滑动窗口大小为64
train_ratio=0.8 #训练集比例
window_size=64 #滑动窗口大小,即循环神经网络的时间步长
X_train,y_train,X_test,y_test=get_traintest(np.array(data0).reshape(-1,),window_size=window_size,train_ratio=train_ratio)
print(X_train.shape,y_train.shape,X_test.shape,y_test.shape)
可以看到上面数据X还是2维的,下面归一化
#归一化
scaler = MinMaxScaler()
scaler = scaler.fit(X_train)
X_train=scaler.transform(X_train)
X_test=scaler.transform(X_test)y_train_orage=y_train.copy()
y_scaler = MinMaxScaler()
y_scaler = y_scaler.fit(y_train.reshape(-1,1))
y_train=y_scaler.transform(y_train.reshape(-1,1))
转为3维
X_train=X_train.reshape(X_train.shape[0],X_train.shape[1],1)
X_test=X_test.reshape(X_test.shape[0],X_test.shape[1],1)
y_test=y_test.reshape(-1,1) ; test_size=y_test.shape[0]
print(X_train.shape,y_train.shape,X_test.shape,y_test.shape)
画图展示:
plt.figure(figsize=(10,5),dpi=256)
plt.plot(data0.index[:-test_size],data0.iloc[:-test_size],label='Train',color='#FA9905')
plt.plot(data0.index[-test_size:],data0.iloc[-(test_size):],label='Test',color='#FB8498',linestyle='dashed')
plt.legend()
plt.ylabel('Predict Series',fontsize=16)
plt.xlabel('Time',fontsize=16)
plt.show()
定义评价指标
回归问题,总是用这四个指标:mae,rmse,mape,R2
定义随机数种子和计算评价指标的函数,当然我这里就没要R2了,用的mse,是一样的,要用R2就改一下下面的函数就行了。
def set_my_seed():os.environ['PYTHONHASHSEED'] = '0'np.random.seed(1)rn.seed(12345)tf.random.set_seed(123)def evaluation(y_test, y_predict):mae = mean_absolute_error(y_test, y_predict)mse = mean_squared_error(y_test, y_predict)rmse = np.sqrt(mean_squared_error(y_test, y_predict))mape=(abs(y_predict -y_test)/ y_test).mean()#r_2=r2_score(y_test, y_predict)return mse, rmse, mae, mape
构建ma1模型:
### 基准预测情况
result = pd.DataFrame()
result['t'] = pd.Series(data0.iloc[:,0])
# 生成第1列到第10列,每一列是t+1到t+10滑动窗口的值
for i in range(1, 6):result[f't-{i}'] = result['t'].shift(i)
result=result.dropna()for t in result.columns[1:]:score=list(evaluation(result['t'], result[t]))s=[round(i,3) for i in score]print(f'{t}的预测效果为:RMSE:{s[0]},MAE:{s[1]},MAPE:{s[2]},R2:{s[3]}')
可以看到MA1模型的RMSE:1.791,MAE:1.338,MAPE:0.768,R2:0.013,很低。
构建模型
下面构建我们的神经网络模型,由于要用transformer层,所以我们需要自定义很多东西:
class AttentionLayer(Layer): #自定义注意力层def __init__(self, **kwargs):super(AttentionLayer, self).__init__(**kwargs)def build(self, input_shape):self.W = self.add_weight(name='attention_weight',shape=(input_shape[-1], input_shape[-1]),initializer='random_normal',trainable=True)self.b = self.add_weight(name='attention_bias',shape=(input_shape[1], input_shape[-1]),initializer='zeros',trainable=True)super(AttentionLayer, self).build(input_shape)def call(self, x):# Applying a simpler attention mechanisme = K.tanh(K.dot(x, self.W) + self.b)a = K.softmax(e, axis=1)output = x * areturn outputdef compute_output_shape(self, input_shape):return input_shape#from __future__ import print_function
from keras import backend as K
from keras.layers import Layer
from tensorflow.keras import layers
from tensorflow import kerasclass PositionEncoding(Layer):def __init__(self, model_dim, **kwargs):self._model_dim = model_dimsuper(PositionEncoding, self).__init__(**kwargs)def call(self, inputs):seq_length = inputs.shape[1]position_encodings = np.zeros((seq_length, self._model_dim))for pos in range(seq_length):for i in range(self._model_dim):position_encodings[pos, i] = pos / np.power(10000, (i-i%2) / self._model_dim)position_encodings[:, 0::2] = np.sin(position_encodings[:, 0::2]) # 2iposition_encodings[:, 1::2] = np.cos(position_encodings[:, 1::2]) # 2i+1position_encodings = K.cast(position_encodings, 'float32')return position_encodingsdef compute_output_shape(self, input_shape):return input_shape
class Add(Layer):def __init__(self, **kwargs):super(Add, self).__init__(**kwargs)def call(self, inputs):input_a, input_b = inputsreturn input_a + input_bdef compute_output_shape(self, input_shape):return input_shape[0]class TransformerEncoder(layers.Layer):def __init__(self, embed_dim, dense_dim, num_heads, **kwargs):super().__init__(**kwargs)self.embed_dim = embed_dimself.dense_dim = dense_dimself.num_heads = num_headsself.attention = layers.MultiHeadAttention(num_heads=num_heads, key_dim=embed_dim)self.dense_proj = keras.Sequential([layers.Dense(dense_dim, activation="relu"),layers.Dense(embed_dim),] )self.layernorm_1 = layers.LayerNormalization()self.layernorm_2 = layers.LayerNormalization()def call(self, inputs, mask=None):if mask is not None:mask = mask[:, tf.newaxis, :]attention_output = self.attention(inputs, inputs, attention_mask=mask)proj_input = self.layernorm_1(inputs + attention_output)proj_output = self.dense_proj(proj_input)return self.layernorm_2(proj_input + proj_output)def get_config(self):config = super().get_config()config.update({"embed_dim": self.embed_dim,"num_heads": self.num_heads,"dense_dim": self.dense_dim,})return config
构建模型函数:
def build_model(X_train,mode='LSTM',hidden_dim=[64,32]):set_my_seed()if mode=='MLP':model = Sequential()model.add(Flatten())model.add(Dense(hidden_dim[0],activation='relu',input_shape=(X_train.shape[-2],X_train.shape[-1])))model.add(Dense(hidden_dim[1],activation='relu'))#model.add(Dense(16,activation='relu'))model.add(Dense(1))elif mode=='RNN':model = Sequential()model.add(SimpleRNN(hidden_dim[0],return_sequences=True, input_shape=(X_train.shape[-2],X_train.shape[-1])))model.add(Dropout(0.2))model.add(SimpleRNN(hidden_dim[1])) model.add(Dropout(0.2))model.add(Dense(1))elif mode=='CNN':model = Sequential()model.add(Conv1D(hidden_dim[0],X_train.shape[-2]-2,activation='relu',input_shape=(X_train.shape[-2],X_train.shape[-1])))model.add(GlobalMaxPooling1D())model.add(Dense(hidden_dim[1],activation='relu'))model.add(Dense(1))elif mode=='LSTM':model = Sequential()model.add(LSTM(hidden_dim[0],return_sequences=True, input_shape=(X_train.shape[-2],X_train.shape[-1])))model.add(Dropout(0.2))model.add(LSTM(hidden_dim[1]))model.add(Dropout(0.2))model.add(Dense(1))elif mode=='GRU':model = Sequential()model.add(GRU(hidden_dim[0],return_sequences=True, input_shape=(X_train.shape[-2],X_train.shape[-1])))model.add(Dropout(0.2))model.add(GRU(hidden_dim[1]))model.add(Dropout(0.2))model.add(Dense(1))elif mode=='BiLSTM':model = Sequential()model.add(Bidirectional(LSTM(hidden_dim[0],return_sequences=True, input_shape=(X_train.shape[-2],X_train.shape[-1]))))model.add(Dropout(0.2))model.add(Bidirectional(LSTM(hidden_dim[1])))model.add(Dropout(0.2))model.add(Dense(1))elif mode=='BiGRU':model = Sequential()model.add(Bidirectional(GRU(hidden_dim[0],return_sequences=True, input_shape=(X_train.shape[-2],X_train.shape[-1]))))model.add(Dropout(0.2))model.add(Bidirectional(GRU(hidden_dim[1])))model.add(Dropout(0.2))model.add(Dense(1))elif mode == 'BiGRU-Attention':model = Sequential()model.add(GRU(hidden_dim[0], return_sequences=True, input_shape=(X_train.shape[-2], X_train.shape[-1])))model.add(AttentionLayer())# Adding normalization and dropout for better training stability and performancemodel.add(LayerNormalization())#model.add(Dropout(0.1))model.add(GRU(hidden_dim[1]))model.add(Dense(1))elif mode == 'BiLSTM-Attention':model = Sequential()model.add(Bidirectional(LSTM(hidden_dim[0], return_sequences=True), input_shape=(X_train.shape[-2], X_train.shape[-1])))model.add(AttentionLayer())model.add(LayerNormalization())model.add(Dropout(0.2))model.add(Bidirectional(LSTM(hidden_dim[1])))#model.add(Flatten())model.add(Dense(hidden_dim[1],activation='relu'))model.add(Dense(1))elif mode=='CNN+BiLSTM': model = Sequential()model.add(Conv1D(filters=hidden_dim[0], kernel_size=3, padding="same",activation="relu"))model.add(MaxPooling1D(pool_size=2))model.add(Bidirectional(LSTM(hidden_dim[1])))model.add(Dense(1))elif mode == 'TCN':model = Sequential()for dilation_rate in [1, 2]:model.add(Conv1D(filters=hidden_dim[0], kernel_size=2, dilation_rate=dilation_rate, padding='causal', activation='relu', input_shape=(X_train.shape[-2], X_train.shape[-1])))model.add(Flatten())model.add(Dense(1))elif mode=='Transformer':model = Sequential()inputs = Input(shape=[X_train.shape[-2],X_train.shape[-1]], name="inputs")encodings = PositionEncoding(32)(inputs)encodings = Add()([inputs, encodings])x = TransformerEncoder(32, hidden_dim[1], 2)(encodings) #嵌入维度,全连接层神经元数,多头数x = GlobalAveragePooling1D()(x)#x = Dropout(0.2)(x)#x = Dense(32, activation='relu')(x)outputs = Dense(1)(x)model = Model(inputs=[inputs], outputs=outputs)else:raise ValueError("Unsupported mode: " + mode)model.compile(optimizer='Adam', loss='mse' ,metrics=[tf.keras.metrics.RootMeanSquaredError(),"mape","mae"])return model
定义一些画图展示用的函数
def plot_loss(hist,imfname=''):plt.subplots(1,4,figsize=(16,2))for i,key in enumerate(hist.history.keys()):n=int(str('14')+str(i+1))plt.subplot(n)plt.plot(hist.history[key], 'k', label=f'Training {key}')plt.title(f'{imfname} Training {key}')plt.xlabel('Epochs')plt.ylabel(key)plt.legend()plt.tight_layout()plt.show()
def plot_fit(y_test, y_pred):plt.figure(figsize=(4,2))plt.plot(y_test, color="red", label="actual")plt.plot(y_pred, color="blue", label="predict")plt.title(f"拟合值和真实值对比")plt.xlabel("Time")plt.ylabel('power')plt.legend()plt.show()
定义训练函数,初始化两个数据框,用于储存我们的误差评价指标和预测值,我们会在序列函数里面进行模型的训练,预测,误差评价指标的计算,以及储存。
df_eval_all=pd.DataFrame(columns=['MSE','RMSE','MAE','MAPE'])
df_preds_all=pd.DataFrame()
def train_fuc(mode='LSTM',batch_size=32,epochs=50,hidden_dim=[32,16],verbose=0,show_loss=True,show_fit=True):#构建模型s = time.time()set_my_seed()model=build_model(X_train=X_train,mode=mode,hidden_dim=hidden_dim)earlystop = EarlyStopping(monitor='loss', min_delta=0, patience=5)hist=model.fit(X_train, y_train,batch_size=batch_size,epochs=epochs,callbacks=[earlystop],verbose=verbose)if show_loss:plot_loss(hist)#预测y_pred = model.predict(X_test)y_pred = y_scaler.inverse_transform(y_pred)#print(f'真实y的形状:{y_test.shape},预测y的形状:{y_pred.shape}')if show_fit:plot_fit(y_test, y_pred)e=time.time()print(f"运行时间为{round(e-s,3)}")df_preds_all[mode]=y_pred.reshape(-1,)s=list(evaluation(y_test, y_pred))df_eval_all.loc[f'{mode}',:]=ss=[round(i,3) for i in s]print(f'{mode}的预测效果为:MSE:{s[0]},RMSE:{s[1]},MAE:{s[2]},MAPE:{s[3]}')print("=======================================运行结束==========================================")return s[0]
初始化参数
window_size=64
batch_size=32
epochs=50
hidden_dim=[32,16]verbose=0
show_fit=True
show_loss=True
mode='LSTM' #MLP,GRU
模型训练
构建MLP模型
train_fuc(mode='MLP',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
上面4个小图分别是训练时候的不同损失的下降情况可以看到基本上40轮以后肯定都收敛了。下面是真实值和预测值对比,然后会打印这些误差指标以及运行时间。
构建RNN模型:
train_fuc(mode='RNN',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
就不展示那么多了,下面我们把所有的模型都一起训练,然后所有的误差指标都会储存起来,我们后面一起查看就可以了。
train_fuc(mode='CNN',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
train_fuc(mode='TCN',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
train_fuc(mode='GRU',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
train_fuc(mode='LSTM',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
train_fuc(mode='BiGRU',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
train_fuc(mode='BiLSTM',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
train_fuc(mode='CNN+BiLSTM',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
train_fuc(mode='BiLSTM-Attention',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
train_fuc(mode='BiGRU-Attention',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
train_fuc(mode='Transformer',batch_size=batch_size,epochs=epochs,hidden_dim=hidden_dim)
有的同学可能惊讶于我不同模型就修改一个mode参数就行了???其他全自动???
我知道大部分同学写代码都是东拼西凑,而且没有很好的编码风格,也不规范,所以说会花费大量的时间在于人的手工调整以及修改中。优雅的写法当然是只修改一个参数就可以把所有重复的过程都再进行一遍,这就是函数的封装性和简洁性的妙用,重复的代码绝对不会重写,重复的工作绝对不会重做。
因为我们写文章,无非都是训练不同的模型预测结果进行评价对比,这都是重复的工作,只是说模型不一样罢了。所有的过程都是重复性的过程,自然就要用代码去消灭这些重复性的工作,就得把他们封装的特别好,简洁和易用。
查看评价指标
我们直接按照MSE排个序:
df_eval_all.loc['MA1',:]=evaluation(result['t'], result['t-1'])
df_eval_all.sort_values('MSE',ascending=True).style.bar(color='pink')
可以看到MA1模型误差最低,不直观的话,可以进行可视化:
bar_width = 0.4
colors=['c', 'orange','g', 'tomato','b', 'm', 'y', 'lime', 'k','orange','pink','grey','tan']
fig, ax = plt.subplots(2,2,figsize=(8,6),dpi=128)
for i,col in enumerate(df_eval_all.columns):n=int(str('22')+str(i+1))plt.subplot(n)df_col=df_eval_all[col]m =np.arange(len(df_col))plt.bar(x=m,height=df_col.to_numpy(),width=bar_width,color=colors)##plt.xlabel('Methods',fontsize=12)names=df_col.indexplt.xticks(range(len(df_col)),names,fontsize=10)plt.xticks(rotation=90)plt.ylabel(col,fontsize=14)plt.tight_layout()
#plt.savefig('柱状图.jpg',dpi=512)
plt.show()
什么结果不用多说了吧,虽然transformer是所有神经网络里面表现效果最好的模型,mse最小,也符合常理。但是,所有的神经网络都没有MA1模型的误差低。也就是说,我们费尽千辛万苦,各种复杂的结构层算法构建出来的神经网络居然都不如直接用Xt 的值作为'Xt+1'的预测值这种最简单的MA1模型!!!!
是不是颠覆三观了,也就是说,这么多充斥在学术界和研究界的用神经网络去做时间序列预测的模型的水论文研究,基本上都是无用功。(当然,更高级的模型我没试过不知道,不乱说)
但是没什么人来指出这个问题,听说最近国外在顶会上有人针对这种神经网络预测长时间序列提出了一系列的问题,但是他们的聚焦点还是在于这些误差评价指标不适用于时间序列预测中,并没有意识到神经网络用于时间序列目前的这个构建方法是存在问题的。
好在我们不用那么悲观,虽然没意义,但是90%的论文谁不是为了水论文呢,谁又真的拿去实际生产模型中去部署调用呢?更重要的是所谓的专家,学者,老师,导师,审稿人都对这些一窍不通,也没人发现这其中的问题。
当然,我为什么选着这个油价数据,是因为他是最具有代表性的。我测试了在其他数据上的这些模型的对比表现,我发现ma1模型并不总是最好的,但是它总是能够获得一个中等偏上的水平,也就是说他总能够打败60%以上的神经网络模型,如果你的数据有的还挺适合神经网络的,那就还可以用神经网络做一些有价值的工作吧。
但是一般来说,频率很高的这种强自相关的数据,ma1效果都挺好的。
最后再画一个不同模型预测出来的序列结果的对比图,这也是水文章里面常用的:
总结
神经网络做时间序列预测主要有,单变量单步预测,单变量多步预测,多变量单步预测,多变量多步预测,
本次演示的是最简单的单变量单步预测,对比了10种神经网络模型。["Transformer", "CNN+BiLSTM", "BiGRU-Attention","BiLSTM-Attention", "BiGRU", "BiLSTM", "TCN", "GRU", "CNN", "LSTM","RNN","MLP", ],然后发现所有的神经网络都没有MA1模型的误差低。也就是说,我们费尽千辛万苦,各种复杂的结构层算法构建出来的神经网络居然都不如直接用Xt 的值作为'Xt+1'的预测值这种最简单的MA1模型。
但是也不用太悲观,在有的数据上表现ma1不一定最好,并且大部分专家,审稿人,导师都不懂,国内也没有人指出这个问题,所以目前水论文还是可以随便放心的用。并且知道这个东西没啥意义就好,要是真的以为自己做了个模型能够产生多少价值跟收益,那可太天真了。
各种模态分解优化算法,损失函数缝合不同的神经网络预测时间序列的模型在往期文章中都有:
Python数据分析案例24——基于深度学习的锂电池寿命预测_锂离子电池寿命预测
Python数据分析案例25——海上风力发电预测(多变量循环神经网络)
Python数据分析案例41——基于CNN-BiLSTM的沪深300收盘价预测
Python数据分析案例42——基于Attention-BiGRU的时间序列数据预测
Python数据分析案例44——基于模态分解和深度学习的电负荷量预测(VMD+BiGRU+注意力)
Python数据分析案例50——基于EEMD-LSTM的石油价格预测
Python数据分析案例52——基于SSA-LSTM的风速预测(麻雀优化)
怎么水论文里面也写的非常清楚。代码都是类似的,框架高度封装,换个数据就能用,不需要怎么修改。
随便组合缝合都能发SCI,毕业真的太容易了有木有。
创作不易,看官觉得写得还不错的话点个关注和赞吧,本人会持续更新python数据分析领域的代码文章~(需要定制类似的代码可私信)
相关文章:
Python数据分析案例70——基于神经网络的时间序列预测(滞后性的效果,预测中存在的问题)
背景 这篇文章可以说是基于 现代的一些神经网络的方法去做时间序列预测的一个介绍科普,也可以说是一个各种模型对比的案例,但也会谈一谈自己做了这么久关于神经网络的时间序列预测的论文,其中一些常见的模式及它们存在的问题以及效果&#x…...
Kotlin Bytedeco OpenCV 图像图像54 透视变换 图像矫正
Kotlin Bytedeco OpenCV 图像图像54 透视变换 图像矫正 1 添加依赖2 测试代码3 测试结果 在OpenCV中,仿射变换(Affine Transformation)和透视变换(Perspective Transformation)是两种常用的图像几何变换方法。 变换方…...
PyTorch使用教程(11)-cuda的使用方法
1. 基本概念 CUDA(Compute Unified Device Architecture)是NVIDIA开发的一种并行计算平台和编程模型,专为图形处理器(GPU)设计,旨在加速科学计算、工程计算和机器学习等领域的高性能计算任务。CUDA允许开发…...
微软开源AI Agent AutoGen 详解
AutoGen是微软发布的一个用于构建AI Agent系统的开源框架,旨在简化事件驱动、分布式、可扩展和弹性Agent应用程序的创建过程。 开源地址: GitHub - microsoft/autogen: A programming framework for agentic AI 🤖 PyPi: autogen-agentchat Discord: https://aka.ms/auto…...
上位机工作感想-2024年工作总结和来年计划
随着工作年限的增增长,发现自己越来越不喜欢在博客里面写一些掺杂自己感想的东西了,或许是逐渐被工作逼得“成熟”了吧。2024年,学到了很多东西,做了很多项目,也帮别人解决了很多问题,唯独没有涨工资。来这…...
js: 区分后端返回数字是否为null、‘-’ 或正常number类型数字。
问: 这是我的代码<CountTo v-if!isNaN(Number(item.num))> <span v-else>{{item.num}}</span> 我希望不是null的时候走countTo,是null的时候直接<span>{{item.num}}</span>显示 回答: 最终结果: …...
C#中字符串方法
字符串属性:Lenght 长度比最大索引大1 string str "frerfgd"; 1.可以通过索引,获取字符串中的某一个字符,下标“0,1.......” Console.WriteLine(str[0]);//f Console.WriteLine(str[1]);//r //Console.WriteLine(s…...
flutter在使用gradle时的加速
当我使用了一些过时的插件的时候,遇到了一些问题 比如什么namespace 问题等,因为有些插件库没有更新了,或者最新版本处于测试阶段 于是我就删除这些旧插件(不符合我要求的插件) 于是根据各论坛的解决方法去做了以下的工作 1:项目中删除了这…...
SpringCloud+Vue+Python人工智能(fastAPI,机器学习,深度学习)前后端架构各功能实现思路——主目录(持续更新)
随着公司业务的增加,公司需要一个javapython人工智能相互配合架构,正常网站业务用java来做,而ai,例如电价预测等回归任务,以及大模型预测全网负荷,新能源出力等任务,使用python通过fastapi暴露接…...
【GIS操作】使用ArcGIS Pro进行海图的地理配准(附:墨卡托投影对比解析)
文章目录 一、应用场景二、墨卡托投影1、知识点2、Arcgis中的坐标系选择 三、操作步骤1、数据转换2、数据加载3、栅格投影4、地理配准 一、应用场景 地理配准是数字化之前必须进行的一项工作。扫描得到的地图数据通常不包含空间参考信息,需要通过具有较高位置精度的…...
C#,入门教程(03)——Visual Studio 2022编写彩色Hello World与动画效果
C#,入门教程(01)—— Visual Studio 2022 免费安装的详细图文与动画教程https://blog.csdn.net/beijinghorn/article/details/123350910 C#,入门教程(02)—— Visual Studio 2022开发环境搭建图文教程https://blog.csdn.net/beijinghorn/article/detail…...
使用 ChatGPT 生成和改进你的论文
文章目录 零、前言一、操作引导二、 生成段落或文章片段三、重写段落四、扩展内容五、生成大纲内容六、提高清晰度和精准度七、解决特定的写作挑战八、感受 零、前言 我是虚竹哥,目标是带十万人玩转ChatGPT。 ChatGPT 是一个非常有用的工具,可以帮助你…...
Unity2021.3.13崩溃的一种情况
如果出现如下的报错,可能是软件冲突的原因。自己的原因是使用f.lux这款软件似乎和Unity相互冲突,出现下面报错。 错误信息如上图...
通过ssh连接debian
使用方法 ssh usernameipaddress [inputpasswd]root用户默认无法由ssh连接, 可以通过修改配置 sudo vim /etc/ssh/sshd_config去掉PermitRootLogin前的‘#’,并修改为 PermitRootLogin yes 重启sshd服务 sudo systemctl restart sshd参考 https://linuxconfig.or…...
Transformer 与注意力机制原理说明与面试笔试题
文章大纲 注意力机制不同 种类的注意力机制Encoder Decoder 框架自注意力自注意力机制概述**基本原理****优点与应用****自注意力与多头注意力的关系****总结**多头注意力**多头注意力机制概述****基本原理****计算过程****多头注意力的优势****多头自注意力与多头注意力的区别…...
C语言之装甲车库车辆动态监控辅助记录系统
🌟 嗨,我是LucianaiB! 🌍 总有人间一两风,填我十万八千梦。 🚀 路漫漫其修远兮,吾将上下而求索。 C语言之装甲车库车辆动态监控辅助记录系统 目录 一、前言 1.1 (一)…...
先进制造aps专题二十七 西门子opcenter aps架构分析
欧美的商业aps,主要就是sap apo,西门子opcenter aps,达索quintiq 从技术的层面,西门子aps是不如sap apo的,但是西门子aps是西门子数字化工厂产品的核心,有很多特色,所以分析 西门子aps主要分计划器和排产器两个部分 计…...
C++/QT环境下图像在窗口下等比例渲染绘制
本文中通过QT获取到opengl上下文环境,通过opengl3.0API将图像等比例渲染到QOpenGLWidget组件上面,相比cpu,渲染能力更强。 有以下四步骤。opengl基本知识点可参照之前文章OPENGL初学习 定义opengl渲染表面属性通过initializeGL接口初始化opengl上下文环境,然后加载并链接着…...
npm ERR! code CERT_HAS_EXPIRED
很不幸看到这个提示。 查了很多网上的解决方案,都提到一个解决方案: npm install -g npmlatest 靠就是执行install报的错,你要我通过install来解决这个问题。可见大多数人都是转发,从不自己试试。 第二个是看系统时间。这个基…...
[系统安全] 六十一.恶意软件分析 (12)LLM赋能Lark工具提取XLM代码的抽象语法树(初探)
您可能之前看到过我写的类似文章,为什么还要重复撰写呢?只是想更好地帮助初学者了解病毒逆向分析和系统安全,更加成体系且不破坏之前的系列。因此,我重新开设了这个专栏,准备系统整理和深入学习系统安全、逆向分析和恶意代码检测,“系统安全”系列文章会更加聚焦,更加系…...
网络Web存储之LocalStorage
文章目录 LocalStorage介绍定义特点兼容性常用方法存值取值删除指定键值对清空所有键值对通过索引获取键名获取所有值判断是否含有某个键(key)拓展遍历得到key存储和读取复杂类型的数据 应用场景 LocalStorage介绍 定义 LocalStorage 是HTML5提供的一种…...
HTML知识点复习
1.src 和 href 的区别 src:表示对资源的引用, src指向的内容会嵌入到其标签里。 当浏览器解析到该元素时候,会暂停其他资源的下载和处理, 直到将该资源加载、编译、执行完毕,所以js脚本一般会放在页面底部 href&…...
【Leetcode 热题 100】45. 跳跃游戏 II
问题背景 给定一个长度为 n n n 的 0 0 0 索引 整数数组 n u m s nums nums。初始位置为 n u m s [ 0 ] nums[0] nums[0]。 每个元素 n u m s [ i ] nums[i] nums[i] 表示从索引 i i i 向前跳转的最大长度。换句话说,如果你在 n u m s [ i ] nums[i] nums[i…...
《offer 来了:Java 面试核心知识点精讲 -- 原理篇》
在 Java 面试的战场上,只知皮毛可不行,面试官们越来越看重对原理的理解。今天就给大家分享一本能让你在面试中脱颖而出的 “武林秘籍”——《offer 来了:Java 面试核心知识点精讲 -- 原理篇》。 本书详细介绍了Java架构师在BAT和移动互联网公司面试中常被问及的核心知识,内…...
Spring Boot中的自动配置原理是什么
Spring Boot 自动配置原理 Spring Boot 的自动配置机制基于 条件化配置,通过 EnableAutoConfiguration 注解来启用。自动配置的核心原理是 基于类路径和环境条件来推断所需要的配置,Spring Boot 会根据项目中引入的依赖和当前环境来自动装配相关的配置项…...
蓝桥杯3525 公因数匹配 | 枚举+数学
题目传送门 这个题目是一个数学题,由于只需要找到存在大于1的公因数的两数,所以比较方便的做法是统计每一个数的(质)因数。可以通过筛法统计质因数降低复杂度,但是直接枚举因数也可以满足要求。使用字典记录每个因数出…...
elasticsearch基础
分布式搜索引擎01 1. 初始elasticsearch 1.1. 了解ES 1.1.1. elasticsearch的作用 elasticsearch是一款非常强大的开源搜索引擎,具备非常多强大功能,可以帮助我们从海量数据中快速找到需要的内容 例如: 在github搜索代码: 在电…...
【JsonViewer】Json格式化
使用 Notepad 对 Json 数据进行格式化处理,使数据在结构上更清晰 1.在线安装 安装之后,重启应用,在插件菜单栏即可看到 JsonViewer 选项,在 Notepad 中放入 Json 数据,点击 Format Json 进行数据格式化 2.离线安装 …...
线性代数概述
矩阵与线性代数的关系 矩阵是线性代数的研究对象之一: 矩阵(Matrix)是一个按照长方阵列排列的复数或实数集合,是线性代数中的核心概念之一。矩阵的定义和性质构成了线性代数中矩阵理论的基础,而矩阵运算则简洁地表示和…...
计算机毕业设计Python+卷积神经网络租房推荐系统 租房大屏可视化 租房爬虫 hadoop spark 58同城租房爬虫 房源推荐系统
温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 温馨提示:文末有 CSDN 平台官方提供的学长联系方式的名片! 作者简介:Java领…...
Linux(DISK:raid5、LVM逻辑卷)
赛题拓扑: 题目: DISK 添加4块大小均为10G的虚拟磁盘,配置raid-5磁盘。创建LVM命名为/dev/vg01/lv01,大小为20G,格式化为ext4,挂在到本地目录/webdata,在分区内建立测试空文件disk.txt。[root@storagesrv ~]# yum install mdadm -y [root@storagesrv ~]# mdadm -C -n …...
RIME-CNN-LSTM-Attention多变量多步时序预测Matlab实现
SCI一区级 | Matlab实现RIME-CNN-LSTM-Multihead-Attention多变量多步时序预测 目录 SCI一区级 | Matlab实现RIME-CNN-LSTM-Multihead-Attention多变量多步时序预测预测效果基本介绍程序设计参考资料 预测效果 基本介绍 1.Matlab实现RIME-CNN-LSTM-Multihead-Attention霜冰算法…...
机器学习中的方差与偏差
文章目录 方差与偏差1.1 数据1.1.1 数据的分布1.1.2 拟合 1.2 方差与偏差1.2.1 泛化误差的拆分1.2.2 理解方差偏差 1.3 方差-偏差trade-off1.3.1 方差-偏差trade-off1.3.2 方差与偏差诊断 1.4 降低策略1.4.1 噪声1.4.2 高偏差1.4.3 高方差 方差与偏差 1.1 数据 1.1.1 数据的分…...
Ubuntu 22.04虚拟机安装配置调整(语言输入法字体共享剪切板等等
2025.01.07安装配置Ubuntu 22.04 记一下 快捷键 截屏 在设置-键盘-快捷键查看 跟搜到的不一样…不过shiftprint感觉也够用 安装 用的是VMware 参考:VMware中安装配置Ubuntu(2024最新版 超详细) 调教(? 语言 改了…...
[创业之路-255]:《华为数字化转型之道》-1-主要章节、核心内容、核心思想
目录 前言:数字化转型对于企业而言,是一种全方位的变革 一、主要章节 1、认知篇(第1~2章)- Why 2、方法篇(第3~5章)- How 3、实践篇(第6~10章)- 实践 4、平台篇(第…...
Java 接口安全指南
Java 接口安全指南 概述 在现代 Web 应用中,接口(API)是前后端交互的核心。然而,接口的安全性常常被忽视,导致数据泄露、未授权访问等安全问题。本文将详细介绍 Java 中如何保障接口安全,涵盖以下内容&am…...
Redis学习笔记1【数据类型和常用命令】
Redis学习笔记 基础语法 1.数据类型 String: 最基本的类型,可以存储任何数据,例如文本或数字。示例值为 hello world。Hash: 用于存储键值对,适合存储对象或结构体。示例值为 {"name": "Jack", "age": 21}。…...
Oracle graph 图数据库体验-安装篇
服务端安装 环境准备 安装数据库 DOCKER 安装23AI FREE ,参考: https://container-registry.oracle.com/ords/f?p113:4:111381387896144:::4:P4_REPOSITORY,AI_REPOSITORY,AI_REPOSITORY_NAME,P4_REPOSITORY_NAME,P4_EULA_ID,P4_BUSINESS_AREA_ID:1…...
Android 13 动态显示隐藏 HomeButton,RecentsButton
com.android.launcher3.taskbar.NavbarButtonsViewController.initButtons mEnabledValue状态<T> StatePropertyHolder(T target, IntPredicate enabledCondition,Property<T, Float> property, float enabledValue, float disabledValue) {mEnableCondition = ena…...
前端开发中的状态管理与网络请求封装
本文将对比 Vuex 和 Pinia 在状态管理中的使用,并介绍如何封装 Axios 进行网络请求。此外,我们还将讨论动态路由、404 页面跳转以及面包屑导航的实现。 话不多说,正文开始~~~ 一、状态管理:Vuex 与 Pinia 对比 1. Vuex Vuex 是…...
AI 大爆发时代,音视频未来路在何方?
AI 大模型突然大火了 回顾2024年,计算机领域最大的变革应该就是大模型进一步火爆了。回顾下大模型的发展历程: 萌芽期:(1950-2005) 1956年:计算机专家约翰麦卡锡首次提出“人工智能”概念,标志…...
30分钟内搭建一个全能轻量级springboot 3.4 + 脚手架 <5> 5分钟集成好caffeine并使用注解操作缓存
快速导航 <1> 5分钟快速创建一个springboot web项目 <2> 5分钟集成好最新版本的开源swagger ui,并使用ui操作调用接口 <3> 5分钟集成好druid并使用druid自带监控工具监控sql请求 <4> 5分钟集成好mybatisplus并使用mybatisplus generator自…...
STM32 FreeRTOS中断管理
目录 FreeRTOS的中断管理 1、STM32中断优先级管理 2、FreeRTOS任务优先级管理 3、寄存器和内存映射寄存器 4、BASEPRI寄存器 5、FreeRTOS与STM32中断管理结合使用 vPortRaiseBASEPRI vPortSetBASEPRI 6、FromISR后缀 7、在中断服务函数中调用FreeRTOS的API函数需注意 F…...
第10章:Python TDD优化货币类方法与引入工厂方法
写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…...
嵌入式硬件篇---基本组合逻辑电路
文章目录 前言基本逻辑门电路1.与门(AND Gate)2.或门(OR Gate)3.非门(NOT Gate)4.与非门(NAND Gate)5.或非门(NOR Gate)6.异或门(XOR Gate&#x…...
回归人文主义,探寻情感本质:从文艺复兴到AI时代,我的情感探索之旅
回归人文主义,探寻情感本质:从文艺复兴到AI时代,我们的情感探索之旅 多年来,我们的团队一直关注人工智能,尤其是AI在音乐领域的应用研究。随着技术的不断演进,我们也不断反思:在“算法、代码、…...
接上回--综合AIDemo测试
一,前言 上回外挂了知识库之后,我们需要使用知识库中的信息,让AI为我们实际处理业务上的需求。 这里我们让AI扮演公司的人事助手,帮我们处理员工请假的业务。 具体流程如下 感知用户需要请假提取用户请假信息获取用户数据库中…...
几何数据结构之四叉树与八叉树
几何数据结构之四叉树与八叉树 四叉树的定义四叉树深度的计算公式推导假设:计算过程:1. 划分空间:2. 节点容纳的最小距离:3. 解出深度:4. 考虑常数项: 总结: 八叉树 四叉树的定义 四叉树&#…...
postman请求参数化
postman界面介绍 一、使用环境变量(Environment Variables)进行参数化 1、在请求中使用环境变量 在请求的url、请求头(Headers)、请求体(Body)等部分都可以使用环境变量。 URL 部分示例 点击 Postman 界面右上角的 “眼睛” 图标(Environment Quick Look)打开环境管理…...
java实现word转html(支持docx及doc文件)
private final static String tempPath "C:\\Users\\xxx\\Desktop\\Word2Html\\src\\test\\";//图片及相关文件保存的路径public static void main(String argv[]) {try {JFileChooser fileChooser new JFileChooser();fileChooser.setDialogTitle("Select a …...