{ "cells": [ { "cell_type": "code", "execution_count": 1, "metadata": {}, "outputs": [ { "name": "stderr", "output_type": "stream", "text": [ "C:\\Users\\asus\\AppData\\Roaming\\Python\\Python39\\site-packages\\pandas\\core\\computation\\expressions.py:21: UserWarning: Pandas requires version '2.8.4' or newer of 'numexpr' (version '2.8.3' currently installed).\n", " from pandas.core.computation.check import NUMEXPR_INSTALLED\n", "C:\\Users\\asus\\AppData\\Roaming\\Python\\Python39\\site-packages\\pandas\\core\\arrays\\masked.py:60: UserWarning: Pandas requires version '1.3.6' or newer of 'bottleneck' (version '1.3.5' currently installed).\n", " from pandas.core import (\n" ] } ], "source": [ "from math import sqrt\n", "from numpy import concatenate\n", "from matplotlib import pyplot\n", "import pandas as pd\n", "import numpy as np\n", "from sklearn.preprocessing import MinMaxScaler\n", "from sklearn.preprocessing import LabelEncoder\n", "from sklearn.metrics import mean_squared_error\n", "from tensorflow.keras import Sequential\n", "\n", "from tensorflow.keras.layers import Dense\n", "from tensorflow.keras.layers import LSTM\n", "from tensorflow.keras.layers import Dropout\n", "from sklearn.model_selection import train_test_split\n", "import matplotlib.pyplot as plt" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "这段代码是一个函数 time_series_to_supervised,它用于将时间序列数据转换为监督学习问题的数据集。下面是该函数的各个部分的含义:\n", "\n", "data: 输入的时间序列数据,可以是列表或2D NumPy数组。\n", "n_in: 作为输入的滞后观察数,即用多少个时间步的观察值作为输入。默认值为96,表示使用前96个时间步的观察值作为输入。\n", "n_out: 作为输出的观测数量,即预测多少个时间步的观察值。默认值为10,表示预测未来10个时间步的观察值。\n", "dropnan: 布尔值,表示是否删除具有NaN值的行。默认为True,即删除具有NaN值的行。\n", "函数首先检查输入数据的维度,并初始化一些变量。然后,它创建一个新的DataFrame对象 df 来存储输入数据,并保存原始的列名。接着,它创建了两个空列表 cols 和 names,用于存储新的特征列和列名。\n", "\n", "接下来,函数开始构建特征列和对应的列名。首先,它将原始的观察序列添加到 cols 列表中,并将其列名添加到 names 列表中。然后,它依次将滞后的观察序列添加到 cols 列表中,并构建相应的列名,格式为 (原始列名)(t-滞后时间)。这样就创建了输入特征的部分。\n", "\n", "接着,函数开始构建输出特征的部分。它依次将未来的观察序列添加到 cols 列表中,并构建相应的列名,格式为 (原始列名)(t+未来时间)。\n", "\n", "最后,函数将所有的特征列拼接在一起,构成一个新的DataFrame对象 agg。如果 dropnan 参数为True,则删除具有NaN值的行。最后,函数返回处理后的数据集 agg。" ] }, { "cell_type": "code", "execution_count": 2, "metadata": {}, "outputs": [], "source": [ "def time_series_to_supervised(data, n_in=96, n_out=10,dropnan=True):\n", " \"\"\"\n", " :param data:作为列表或2D NumPy数组的观察序列。需要。\n", " :param n_in:作为输入的滞后观察数(X)。值可以在[1..len(数据)]之间可选。默认为1。\n", " :param n_out:作为输出的观测数量(y)。值可以在[0..len(数据)]之间。可选的。默认为1。\n", " :param dropnan:Boolean是否删除具有NaN值的行。可选的。默认为True。\n", " :return:\n", " \"\"\"\n", " n_vars = 1 if type(data) is list else data.shape[1]\n", " df = pd.DataFrame(data)\n", " origNames = df.columns\n", " cols, names = list(), list()\n", " cols.append(df.shift(0))\n", " names += [('%s' % origNames[j]) for j in range(n_vars)]\n", " n_in = max(0, n_in)\n", " for i in range(n_in, 0, -1):\n", " time = '(t-%d)' % i\n", " cols.append(df.shift(i))\n", " names += [('%s%s' % (origNames[j], time)) for j in range(n_vars)]\n", " n_out = max(n_out, 0)\n", " for i in range(1, n_out+1):\n", " time = '(t+%d)' % i\n", " cols.append(df.shift(-i))\n", " names += [('%s%s' % (origNames[j], time)) for j in range(n_vars)]\n", " agg = pd.concat(cols, axis=1)\n", " agg.columns = names\n", " if dropnan:\n", " agg.dropna(inplace=True)\n", " return agg" ] }, { "cell_type": "code", "execution_count": 3, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " Temp Humidity GHI DHI Rainfall Power\n", "0 19.779453 40.025826 3.232706 1.690531 0.0 0.0\n", "1 19.714937 39.605961 3.194991 1.576346 0.0 0.0\n", "2 19.549330 39.608631 3.070866 1.576157 0.0 0.0\n", "3 19.405870 39.680702 3.038623 1.482489 0.0 0.0\n", "4 19.387363 39.319881 2.656474 1.134153 0.0 0.0\n", "(104256, 6)\n" ] } ], "source": [ "# 加载数据\n", "path1 = r\"D:\\project\\小论文1-基于ICEEMDAN分解的时序高维变化的短期光伏功率预测模型\\CEEMAN-PosConv1dbiLSTM-LSTM\\模型代码流程\\data6.csv\"#数据所在路径\n", "#我的数据是excel表,若是csv文件用pandas的read_csv()函数替换即可。\n", "datas1 = pd.DataFrame(pd.read_csv(path1))\n", "#我只取了data表里的第3、23、16、17、18、19、20、21、27列,如果取全部列的话这一行可以去掉\n", "# data1 = datas1.iloc[:,np.r_[3,23,16:22,27]]\n", "data1=datas1.interpolate()\n", "values1 = data1.values\n", "print(data1.head())\n", "print(data1.shape)" ] }, { "cell_type": "code", "execution_count": 4, "metadata": {}, "outputs": [], "source": [ "# data2= data1.drop(['date','Air_P','RH'], axis = 1)" ] }, { "cell_type": "code", "execution_count": 5, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(104256, 6)\n" ] } ], "source": [ "# 使用MinMaxScaler进行归一化\n", "scaler = MinMaxScaler(feature_range=(0, 1))\n", "scaledData1 = scaler.fit_transform(data1)\n", "print(scaledData1.shape)" ] }, { "cell_type": "code", "execution_count": 6, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ " 0 1 2 3 4 5 0(t-96) \\\n", "96 0.555631 0.349673 0.190042 0.040558 0.0 0.236302 0.490360 \n", "97 0.564819 0.315350 0.211335 0.044613 0.0 0.258204 0.489088 \n", "98 0.576854 0.288321 0.229657 0.047549 0.0 0.279860 0.485824 \n", "99 0.581973 0.268243 0.247775 0.053347 0.0 0.301336 0.482997 \n", "100 0.586026 0.264586 0.266058 0.057351 0.0 0.322851 0.482632 \n", "\n", " 1(t-96) 2(t-96) 3(t-96) ... 2(t-1) 3(t-1) 4(t-1) 5(t-1) \\\n", "96 0.369105 0.002088 0.002013 ... 0.166009 0.036794 0.0 0.214129 \n", "97 0.364859 0.002061 0.001839 ... 0.190042 0.040558 0.0 0.236302 \n", "98 0.364886 0.001973 0.001839 ... 0.211335 0.044613 0.0 0.258204 \n", "99 0.365615 0.001950 0.001697 ... 0.229657 0.047549 0.0 0.279860 \n", "100 0.361965 0.001679 0.001167 ... 0.247775 0.053347 0.0 0.301336 \n", "\n", " 0(t+1) 1(t+1) 2(t+1) 3(t+1) 4(t+1) 5(t+1) \n", "96 0.564819 0.315350 0.211335 0.044613 0.0 0.258204 \n", "97 0.576854 0.288321 0.229657 0.047549 0.0 0.279860 \n", "98 0.581973 0.268243 0.247775 0.053347 0.0 0.301336 \n", "99 0.586026 0.264586 0.266058 0.057351 0.0 0.322851 \n", "100 0.590772 0.258790 0.282900 0.060958 0.0 0.343360 \n", "\n", "[5 rows x 588 columns]\n" ] } ], "source": [ "n_steps_in =96 #历史时间长度\n", "n_steps_out=1#预测时间长度\n", "processedData1 = time_series_to_supervised(scaledData1,n_steps_in,n_steps_out)\n", "print(processedData1.head())" ] }, { "cell_type": "code", "execution_count": 7, "metadata": {}, "outputs": [], "source": [ "data_x = processedData1.loc[:,'0(t-96)':'5(t-1)']#去除power剩下的做标签列\n", "data_y = processedData1.loc[:,'5']" ] }, { "cell_type": "code", "execution_count": 8, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(104159, 576)" ] }, "execution_count": 8, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data_x.shape" ] }, { "cell_type": "code", "execution_count": 9, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "96 0.236302\n", "97 0.258204\n", "98 0.279860\n", "99 0.301336\n", "100 0.322851\n", " ... \n", "104250 0.000000\n", "104251 0.000000\n", "104252 0.000000\n", "104253 0.000000\n", "104254 0.000000\n", "Name: 5, Length: 104159, dtype: float64" ] }, "execution_count": 9, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data_y" ] }, { "cell_type": "code", "execution_count": 10, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(104159,)" ] }, "execution_count": 10, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data_y.shape" ] }, { "cell_type": "code", "execution_count": 11, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "(83328, 96, 6) (83328,) (20831, 96, 6) (20831,)\n" ] } ], "source": [ "# 7.划分训练集和测试集\n", "\n", "test_size = int(len(data_x) * 0.2)\n", "# 计算训练集和测试集的索引范围\n", "train_indices = range(len(data_x) - test_size)\n", "test_indices = range(len(data_x) - test_size, len(data_x))\n", "\n", "# 根据索引范围划分数据集\n", "train_X1 = data_x.iloc[train_indices].values.reshape((-1, n_steps_in, scaledData1.shape[1]))\n", "test_X1 = data_x.iloc[test_indices].values.reshape((-1, n_steps_in, scaledData1.shape[1]))\n", "train_y = data_y.iloc[train_indices].values\n", "test_y = data_y.iloc[test_indices].values\n", "\n", "\n", "# # 多次运行代码时希望得到相同的数据分割,可以设置 random_state 参数为一个固定的整数值\n", "# train_X1,test_X1, train_y, test_y = train_test_split(data_x.values, data_y.values, test_size=0.2, random_state=343)\n", "# reshape input to be 3D [samples, timesteps, features]\n", "train_X = train_X1.reshape((train_X1.shape[0], n_steps_in, scaledData1.shape[1]))\n", "test_X = test_X1.reshape((test_X1.shape[0], n_steps_in,scaledData1.shape[1]))\n", "print(train_X.shape, train_y.shape, test_X.shape, test_y.shape)\n", "# 使用train_test_split函数划分训练集和测试集,测试集的比重是40%。\n", "# 然后将train_X1、test_X1进行一个升维,变成三维,维数分别是[samples,timesteps,features]。\n", "# 打印一下他们的shape:\\\n" ] }, { "cell_type": "code", "execution_count": 12, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(83328, 96, 6)" ] }, "execution_count": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "train_X1.shape" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "WARNING:tensorflow:From d:\\Anaconda3\\lib\\site-packages\\keras\\src\\backend\\tensorflow\\core.py:192: The name tf.placeholder is deprecated. Please use tf.compat.v1.placeholder instead.\n", "\n" ] }, { "data": { "text/html": [ "
Model: \"functional\"\n",
"
\n"
],
"text/plain": [
"\u001b[1mModel: \"functional\"\u001b[0m\n"
]
},
"metadata": {},
"output_type": "display_data"
},
{
"data": {
"text/html": [
"┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓\n", "┃ Layer (type) ┃ Output Shape ┃ Param # ┃ Connected to ┃\n", "┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩\n", "│ input_layer │ (None, 96, 6) │ 0 │ - │\n", "│ (InputLayer) │ │ │ │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ conv1d (Conv1D) │ (None, 95, 64) │ 832 │ input_layer[0][0] │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ max_pooling1d │ (None, 95, 64) │ 0 │ conv1d[0][0] │\n", "│ (MaxPooling1D) │ │ │ │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ bidirectional │ (None, 95, 128) │ 49,920 │ max_pooling1d[0]… │\n", "│ (Bidirectional) │ │ │ │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ attention_with_imp… │ [(None, None, │ 66,304 │ bidirectional[0]… │\n", "│ (AttentionWithImpr… │ 128), (None, 8, │ │ bidirectional[0]… │\n", "│ │ None, None)] │ │ bidirectional[0]… │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ global_average_poo… │ (None, 128) │ 0 │ attention_with_i… │\n", "│ (GlobalAveragePool… │ │ │ │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ dense_4 (Dense) │ (None, 1) │ 129 │ global_average_p… │\n", "└─────────────────────┴───────────────────┴────────────┴───────────────────┘\n", "\n" ], "text/plain": [ "┏━━━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┳━━━━━━━━━━━━┳━━━━━━━━━━━━━━━━━━━┓\n", "┃\u001b[1m \u001b[0m\u001b[1mLayer (type) \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mOutput Shape \u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1m Param #\u001b[0m\u001b[1m \u001b[0m┃\u001b[1m \u001b[0m\u001b[1mConnected to \u001b[0m\u001b[1m \u001b[0m┃\n", "┡━━━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━╇━━━━━━━━━━━━╇━━━━━━━━━━━━━━━━━━━┩\n", "│ input_layer │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m96\u001b[0m, \u001b[38;5;34m6\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │ - │\n", "│ (\u001b[38;5;33mInputLayer\u001b[0m) │ │ │ │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ conv1d (\u001b[38;5;33mConv1D\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m95\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m832\u001b[0m │ input_layer[\u001b[38;5;34m0\u001b[0m][\u001b[38;5;34m0\u001b[0m] │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ max_pooling1d │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m95\u001b[0m, \u001b[38;5;34m64\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │ conv1d[\u001b[38;5;34m0\u001b[0m][\u001b[38;5;34m0\u001b[0m] │\n", "│ (\u001b[38;5;33mMaxPooling1D\u001b[0m) │ │ │ │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ bidirectional │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m95\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m49,920\u001b[0m │ max_pooling1d[\u001b[38;5;34m0\u001b[0m]… │\n", "│ (\u001b[38;5;33mBidirectional\u001b[0m) │ │ │ │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ attention_with_imp… │ [(\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;45mNone\u001b[0m, │ \u001b[38;5;34m66,304\u001b[0m │ bidirectional[\u001b[38;5;34m0\u001b[0m]… │\n", "│ (\u001b[38;5;33mAttentionWithImpr…\u001b[0m │ \u001b[38;5;34m128\u001b[0m), (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m8\u001b[0m, │ │ bidirectional[\u001b[38;5;34m0\u001b[0m]… │\n", "│ │ \u001b[38;5;45mNone\u001b[0m, \u001b[38;5;45mNone\u001b[0m)] │ │ bidirectional[\u001b[38;5;34m0\u001b[0m]… │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ global_average_poo… │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m128\u001b[0m) │ \u001b[38;5;34m0\u001b[0m │ attention_with_i… │\n", "│ (\u001b[38;5;33mGlobalAveragePool…\u001b[0m │ │ │ │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ dense_4 (\u001b[38;5;33mDense\u001b[0m) │ (\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;34m1\u001b[0m) │ \u001b[38;5;34m129\u001b[0m │ global_average_p… │\n", "└─────────────────────┴───────────────────┴────────────┴───────────────────┘\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Total params: 117,185 (457.75 KB)\n", "\n" ], "text/plain": [ "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m117,185\u001b[0m (457.75 KB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Trainable params: 117,185 (457.75 KB)\n", "\n" ], "text/plain": [ "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m117,185\u001b[0m (457.75 KB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Non-trainable params: 0 (0.00 B)\n", "\n" ], "text/plain": [ "\u001b[1m Non-trainable params: \u001b[0m\u001b[38;5;34m0\u001b[0m (0.00 B)\n" ] }, "metadata": {}, "output_type": "display_data" } ], "source": [ "import tensorflow as tf\n", "from tensorflow.keras.layers import Input, Conv1D, Bidirectional, GlobalAveragePooling1D, Dense, GRU, MaxPooling1D\n", "from tensorflow.keras.models import Model\n", "from tensorflow.keras.initializers import RandomUniform\n", "class AttentionWithImproveRelativePositionEncoding(tf.keras.layers.Layer):\n", " def __init__(self, d_model, num_heads, max_len=5000):\n", " super(AttentionWithImproveRelativePositionEncoding, self).__init__()\n", " self.num_heads = num_heads\n", " self.d_model = d_model\n", " self.max_len = max_len\n", " self.wq = tf.keras.layers.Dense(d_model)\n", " self.wk = tf.keras.layers.Dense(d_model)\n", " self.wv = tf.keras.layers.Dense(d_model)\n", " self.dense = tf.keras.layers.Dense(d_model)\n", " self.position_encoding = ImproveRelativePositionEncoding(d_model)\n", "\n", " def call(self, v, k, q, mask):\n", " batch_size = tf.shape(q)[0]\n", " q = self.wq(q)\n", " k = self.wk(k)\n", " v = self.wv(v)\n", "\n", " # 添加位置编码\n", " k += self.position_encoding (k)\n", " q += self.position_encoding (q)\n", "\n", " q = self.split_heads(q, batch_size)\n", " k = self.split_heads(k, batch_size)\n", " v = self.split_heads(v, batch_size)\n", "\n", " scaled_attention, attention_weights = self.scaled_dot_product_attention(q, k, v, mask)\n", " scaled_attention = tf.transpose(scaled_attention, perm=[0, 2, 1, 3])\n", " concat_attention = tf.reshape(scaled_attention, (batch_size, -1, self.d_model))\n", " output = self.dense(concat_attention)\n", " return output, attention_weights\n", "\n", " def split_heads(self, x, batch_size):\n", " x = tf.reshape(x, (batch_size, -1, self.num_heads, self.d_model // self.num_heads))\n", " return tf.transpose(x, perm=[0, 2, 1, 3])\n", "\n", " def scaled_dot_product_attention(self, q, k, v, mask):\n", " matmul_qk = tf.matmul(q, k, transpose_b=True)\n", " dk = tf.cast(tf.shape(k)[-1], tf.float32)\n", " scaled_attention_logits = matmul_qk / tf.math.sqrt(dk)\n", "\n", " if mask is not None:\n", " scaled_attention_logits += (mask * -1e9)\n", "\n", " attention_weights = tf.nn.softmax(scaled_attention_logits, axis=-1)\n", " output = tf.matmul(attention_weights, v)\n", " return output, attention_weights\n", "\n", "class ImproveRelativePositionEncoding(tf.keras.layers.Layer):\n", " def __init__(self, d_model, max_len=5000):\n", " super(ImproveRelativePositionEncoding, self).__init__()\n", " self.max_len = max_len\n", " self.d_model = d_model\n", " # 引入可变化的参数u和v进行线性变化\n", " self.u = self.add_weight(shape=(self.d_model,),\n", " initializer=RandomUniform(),\n", " trainable=True)\n", " self.v = self.add_weight(shape=(self.d_model,),\n", " initializer=RandomUniform(),\n", " trainable=True)\n", " def call(self, inputs):\n", " seq_length = inputs.shape[1]\n", " pos_encoding = self.relative_positional_encoding(seq_length, self.d_model)\n", " \n", " # 调整原始的相对位置编码公式,将u和v参数融入其中\n", " pe_with_params = pos_encoding * self.u+ pos_encoding * self.v\n", " return inputs + pe_with_params\n", "\n", " def relative_positional_encoding(self, position, d_model):\n", " pos = tf.range(position, dtype=tf.float32)\n", " i = tf.range(d_model, dtype=tf.float32)\n", " \n", " angles = 1 / tf.pow(10000.0, (2 * (i // 2)) / tf.cast(d_model, tf.float32))\n", " angle_rads = tf.einsum('i,j->ij', pos, angles)\n", " #保留了sinous机制\n", " # Apply sin to even indices; 2i\n", " angle_rads_sin = tf.sin(angle_rads[:, 0::2])\n", " # Apply cos to odd indices; 2i+1\n", " angle_rads_cos = tf.cos(angle_rads[:, 1::2])\n", "\n", " pos_encoding = tf.stack([angle_rads_sin, angle_rads_cos], axis=2)\n", " pos_encoding = tf.reshape(pos_encoding, [1, position, d_model])\n", "\n", " return pos_encoding\n", "\n", "\n", "\n", "def PosConv1biGRUWithSelfAttention(input_shape, gru_units, num_heads):\n", " inputs = Input(shape=input_shape)\n", " # CNN layer\n", " cnn_layer = Conv1D(filters=64, kernel_size=2, activation='relu')(inputs)\n", " cnn_layer = MaxPooling1D(pool_size=1)(cnn_layer)\n", " gru_output = Bidirectional(GRU(gru_units, return_sequences=True))(cnn_layer)\n", " \n", " # Apply Self-Attention\n", " self_attention =AttentionWithImproveRelativePositionEncoding(d_model=gru_units*2, num_heads=num_heads)\n", " gru_output, _ = self_attention(gru_output, gru_output, gru_output, mask=None)\n", " \n", " pool1 = GlobalAveragePooling1D()(gru_output)\n", " output = Dense(1)(pool1)\n", " \n", " return Model(inputs=inputs, outputs=output)\n", "\n", "\n", "input_shape = (96, 6)\n", "gru_units = 64\n", "num_heads = 8\n", "\n", "# Create model\n", "model = PosConv1biGRUWithSelfAttention(input_shape, gru_units, num_heads)\n", "model.compile(optimizer='adam', loss='mse')\n", "model.summary()\n" ] }, { "cell_type": "code", "execution_count": 14, "metadata": {}, "outputs": [ { "name": "stdout", "output_type": "stream", "text": [ "Epoch 1/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m82s\u001b[0m 61ms/step - loss: 0.0187 - val_loss: 0.0021\n", "Epoch 2/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m80s\u001b[0m 62ms/step - loss: 0.0014 - val_loss: 0.0025\n", "Epoch 3/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m84s\u001b[0m 64ms/step - loss: 0.0013 - val_loss: 0.0021\n", "Epoch 4/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m87s\u001b[0m 67ms/step - loss: 0.0012 - val_loss: 0.0020\n", "Epoch 5/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m76s\u001b[0m 58ms/step - loss: 0.0013 - val_loss: 0.0019\n", "Epoch 6/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m71s\u001b[0m 55ms/step - loss: 0.0011 - val_loss: 0.0020\n", "Epoch 7/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m70s\u001b[0m 54ms/step - loss: 0.0011 - val_loss: 0.0019\n", "Epoch 8/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m81s\u001b[0m 62ms/step - loss: 0.0011 - val_loss: 0.0020\n", "Epoch 9/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m82s\u001b[0m 63ms/step - loss: 0.0012 - val_loss: 0.0019\n", "Epoch 10/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m85s\u001b[0m 65ms/step - loss: 0.0011 - val_loss: 0.0018\n", "Epoch 11/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m88s\u001b[0m 68ms/step - loss: 0.0011 - val_loss: 0.0019\n", "Epoch 12/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m90s\u001b[0m 69ms/step - loss: 0.0011 - val_loss: 0.0018\n", "Epoch 13/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m79s\u001b[0m 61ms/step - loss: 0.0011 - val_loss: 0.0020\n", "Epoch 14/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m83s\u001b[0m 64ms/step - loss: 0.0011 - val_loss: 0.0019\n", "Epoch 15/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m82s\u001b[0m 63ms/step - loss: 0.0011 - val_loss: 0.0018\n", "Epoch 16/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m80s\u001b[0m 61ms/step - loss: 0.0011 - val_loss: 0.0019\n", "Epoch 17/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m81s\u001b[0m 63ms/step - loss: 0.0010 - val_loss: 0.0020\n", "Epoch 18/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m77s\u001b[0m 59ms/step - loss: 0.0011 - val_loss: 0.0018\n", "Epoch 19/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m78s\u001b[0m 60ms/step - loss: 0.0011 - val_loss: 0.0018\n", "Epoch 20/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m77s\u001b[0m 59ms/step - loss: 0.0011 - val_loss: 0.0018\n", "\u001b[1m651/651\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m11s\u001b[0m 17ms/step\n" ] } ], "source": [ "# Compile and train the model\n", "model.compile(optimizer='adam', loss='mean_squared_error')\n", "from keras.callbacks import EarlyStopping, ModelCheckpoint\n", "\n", "# 定义早停机制\n", "early_stopping = EarlyStopping(monitor='val_loss', min_delta=0, patience=10, verbose=0, mode='min')\n", "\n", "# 拟合模型,并添加早停机制和模型检查点\n", "history = model.fit(train_X, train_y, epochs=100, batch_size=64, validation_data=(test_X, test_y), \n", " callbacks=[early_stopping])\n", "# 预测\n", "lstm_pred = model.predict(test_X)\n", "# 将预测结果的形状修改为与原始数据相同的形状" ] }, { "cell_type": "code", "execution_count": 15, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "