{ "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": 4, "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": 5, "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": 10, "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": 11, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(104159, 576)" ] }, "execution_count": 11, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data_x.shape" ] }, { "cell_type": "code", "execution_count": 12, "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": 12, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data_y" ] }, { "cell_type": "code", "execution_count": 13, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(104159,)" ] }, "execution_count": 13, "metadata": {}, "output_type": "execute_result" } ], "source": [ "data_y.shape" ] }, { "cell_type": "code", "execution_count": 14, "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": 15, "metadata": {}, "outputs": [ { "data": { "text/plain": [ "(83328, 96, 6)" ] }, "execution_count": 15, "metadata": {}, "output_type": "execute_result" } ], "source": [ "train_X1.shape" ] }, { "cell_type": "code", "execution_count": 16, "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", "│ self_attention │ [(None, None, │ 66,048 │ bidirectional[0]… │\n", "│ (SelfAttention) │ 128), (None, 8, │ │ bidirectional[0]… │\n", "│ │ None, None)] │ │ bidirectional[0]… │\n", "├─────────────────────┼───────────────────┼────────────┼───────────────────┤\n", "│ global_average_poo… │ (None, 128) │ 0 │ self_attention[0… │\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", "│ self_attention │ [(\u001b[38;5;45mNone\u001b[0m, \u001b[38;5;45mNone\u001b[0m, │ \u001b[38;5;34m66,048\u001b[0m │ bidirectional[\u001b[38;5;34m0\u001b[0m]… │\n", "│ (\u001b[38;5;33mSelfAttention\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 │ self_attention[\u001b[38;5;34m0\u001b[0m… │\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: 116,929 (456.75 KB)\n", "\n" ], "text/plain": [ "\u001b[1m Total params: \u001b[0m\u001b[38;5;34m116,929\u001b[0m (456.75 KB)\n" ] }, "metadata": {}, "output_type": "display_data" }, { "data": { "text/html": [ "
Trainable params: 116,929 (456.75 KB)\n", "\n" ], "text/plain": [ "\u001b[1m Trainable params: \u001b[0m\u001b[38;5;34m116,929\u001b[0m (456.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", "class SelfAttention(tf.keras.layers.Layer):\n", " def __init__(self, d_model, num_heads):\n", " super(SelfAttention, self).__init__()\n", " self.num_heads = num_heads\n", " self.d_model = d_model\n", " assert d_model % self.num_heads == 0\n", " self.depth = d_model // self.num_heads\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", "\n", " def split_heads(self, x, batch_size):\n", " x = tf.reshape(x, (batch_size, -1, self.num_heads, self.depth))\n", " return tf.transpose(x, perm=[0, 2, 1, 3])\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", " 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 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 SelfAttentionWithRelativePositionEncoding(tf.keras.layers.Layer):\n", " def __init__(self, d_model, num_heads, max_len=5000):\n", " super(SelfAttentionWithRelativePositionEncoding, 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.relative_position_encoding = AdvancedRelativePositionalEncoding(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.relative_position_encoding(k)\n", " q += self.relative_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", "import tensorflow as tf\n", "import numpy as np\n", "\n", "import tensorflow as tf\n", "\n", "class AdvancedRelativePositionalEncoding(tf.keras.layers.Layer):\n", " def __init__(self, d_model, max_len=5000):\n", " super(AdvancedRelativePositionalEncoding, self).__init__()\n", " self.max_len = max_len\n", " self.d_model = d_model\n", " # #创新点 引入可变化的参数u,v 进行线性变化\n", " self.u = tf.Variable(tf.random(self.add_weight(shape=(d_model,), initializer='random_normal', trainable=True)))\n", " self.v = tf.Variable(tf.random(self.add_weight(shape=(d_model,), initializer='random_normal', trainable=True)))\n", "\n", " def call(self, inputs):\n", " seq_length = tf.shape(inputs)[1]\n", " pos_encoding = self.relative_positional_encoding(seq_length, self.d_model)\n", "\n", " # 保留Sinusoidal生成方案\n", " angle_rads_sin = pos_encoding[:, :, 0]\n", " angle_rads_cos = pos_encoding[:, :, 1]\n", "\n", " # 线性维度转换层\n", " ti = tf.expand_dims(inputs, axis=1) # shape: [batch_size, 1, seq_length, d_model]\n", " tj = tf.expand_dims(inputs, axis=2) # shape: [batch_size, seq_length, 1, d_model]\n", "\n", " # 计算表征 t_i * W_q * W_k^T * t_j\n", " t_wq_wk_t = tf.einsum('bijd,d->bij', tf.einsum('bijd,d->bijd', ti, self.u), tf.transpose(tj, perm=[0, 1, 3, 2]))\n", "\n", " # 计算基于全局的偏置 t_i * W_q * W_k^T * R_(i-j)^T\n", " t_wq_wk_r = tf.einsum('bijd,d->bij', tf.einsum('bijd,d->bijd', ti, self.u), angle_rads_sin)\n", "\n", " # 计算基于表征的偏置 u * W_q * W_k^T * t_j\n", " E_u = tf.einsum('bd,bijd->bij', self.u, ti)\n", "\n", " # 计算基于表征的局部偏置 v * W_q * W_k^T * R_(i-j)^T\n", " R_v = tf.einsum('bd,bijd->bij', self.v, angle_rads_cos)\n", "\n", " \n", " pe_with_params = t_wq_wk_t + R_v + t_wq_wk_r + E_u\n", "\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", "\n", " pos_encoding = tf.stack([tf.sin(angle_rads[:, 0::2]), tf.cos(angle_rads[:, 1::2])], axis=-1)\n", " pos_encoding = tf.pad(pos_encoding, [[0, 0], [0, 0], [0, 0]]) #embbing维度嵌入层\n", "\n", " return pos_encoding\n", "\n", "\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 = SelfAttention(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": 17, "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[1m90s\u001b[0m 68ms/step - loss: 0.0267 - val_loss: 0.0024\n", "Epoch 2/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m86s\u001b[0m 66ms/step - loss: 0.0015 - val_loss: 0.0026\n", "Epoch 3/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m98s\u001b[0m 76ms/step - loss: 0.0013 - val_loss: 0.0020\n", "Epoch 4/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m95s\u001b[0m 73ms/step - loss: 0.0013 - val_loss: 0.0020\n", "Epoch 5/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m102s\u001b[0m 78ms/step - loss: 0.0012 - val_loss: 0.0018\n", "Epoch 6/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m96s\u001b[0m 74ms/step - loss: 0.0012 - val_loss: 0.0019\n", "Epoch 7/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m98s\u001b[0m 75ms/step - loss: 0.0012 - val_loss: 0.0018\n", "Epoch 8/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m109s\u001b[0m 84ms/step - loss: 0.0011 - val_loss: 0.0018\n", "Epoch 9/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m113s\u001b[0m 87ms/step - loss: 0.0012 - val_loss: 0.0018\n", "Epoch 10/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m115s\u001b[0m 88ms/step - loss: 0.0012 - val_loss: 0.0019\n", "Epoch 11/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m117s\u001b[0m 90ms/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[1m115s\u001b[0m 89ms/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[1m116s\u001b[0m 89ms/step - loss: 0.0011 - val_loss: 0.0019\n", "Epoch 14/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m112s\u001b[0m 86ms/step - loss: 0.0011 - val_loss: 0.0018\n", "Epoch 15/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m114s\u001b[0m 87ms/step - loss: 0.0011 - val_loss: 0.0019\n", "Epoch 16/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m141s\u001b[0m 108ms/step - loss: 0.0011 - val_loss: 0.0020\n", "Epoch 17/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m138s\u001b[0m 106ms/step - loss: 0.0011 - val_loss: 0.0019\n", "Epoch 18/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m120s\u001b[0m 92ms/step - loss: 0.0011 - val_loss: 0.0019\n", "Epoch 19/100\n", "\u001b[1m1302/1302\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m122s\u001b[0m 94ms/step - loss: 0.0010 - val_loss: 0.0021\n", "\u001b[1m651/651\u001b[0m \u001b[32m━━━━━━━━━━━━━━━━━━━━\u001b[0m\u001b[37m\u001b[0m \u001b[1m13s\u001b[0m 19ms/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", "model_pred = model.predict(test_X)\n", "# 将预测结果的形状修改为与原始数据相同的形状" ] }, { "cell_type": "code", "execution_count": 18, "metadata": {}, "outputs": [ { "data": { "image/png": "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", "text/plain": [ "