位置: IT常识 - 正文

利用LSTM实现预测时间序列(股票预测)(lstm输出多个预测值)

编辑:rootadmin
利用LSTM实现预测时间序列(股票预测) 目录1. 作者介绍2. tushare 简介3. LSTM简介3.1 循环神经网络 (Recurrent Neural Networks)3.2 LSTM网络3.2.1 LSTM的核心思想3.2.2 一步一步理解LSTM4. 代码实现4.1 导入相关资源包4.2 定义模型结构4.3 制作数据集4.4 模型训练4.5 测试与保存结果4.6 实验结果5. 完整代码1. 作者介绍

推荐整理分享利用LSTM实现预测时间序列(股票预测)(lstm输出多个预测值),希望有所帮助,仅作参考,欢迎阅读内容。

文章相关热门搜索词:lstm预测模型,lstm预测模型,lstm预测原理,基于lstm 预测 论文,lstm预测原理,lstm预测流程图,lstm预测效果不好怎么调整,lstm预测效果不好怎么调整,内容如对您有帮助,希望把文章链接给更多的朋友!

糜红敏,男,西安工程大学电子信息学院,2019级硕士研究生,张宏伟人工智能课题组。 研究方向:机器视觉与人工智能。 电子邮件:1353197091@qq.com

2. tushare 简介

Tushare是一个免费、开源的python财经数据接口包。主要实现对股票等金融数据从数据采集、清洗加工到数据存储的过程,能够为金融分析人员提供快速、整洁、和多样的便于分析的数据,为他们在数据获取方面极大地减轻工作量,使他们更加专注于策略和模型的研究与实现上。考虑到Python pandas包在金融量化分析中体现出的优势,Tushare返回的绝大部分的数据格式都是pandas DataFrame类型,非常便于用pandas、NumPy、Matplotlib进行数据分析和可视化。 更详细的介绍http://tushare.org/index.html#id3

通过tushare接口下载下来的部分数据的可视化如图所示:

3. LSTM简介3.1 循环神经网络 (Recurrent Neural Networks)

人对一个问题的思考不会完全从头开始。比如你在阅读本片文章的时,你会根据之前理解过的信息来理解下面看到的文字。在理解当前文字的时候,你并不会忘记之前看过的文字,从头思考当前文字的含义。

传统的神经网络并不能做到这一点,这是在对这种序列信息(如语音)进行预测时的一个缺点。比如你想对电影中的每个片段去做事件分类,传统的神经网络是很难通过利用前面的事件信息来对后面事件进行分类。

而循环神经网络(RNN)可以通过不停的将信息循环操作,保证信息持续存在,从而解决上述问题。RNN如下图所示:(原文来自这里) 可以看出A是一组神经网络(可以理解为一个网络的自循环),它的工作是不停的接收 Xt 并且输出 ht。从图中可以看出A允许将信息不停的再内部循环,这样使得它可以保证每一步的计算都保存以前的信息。

更好的理解方式,将RNN的自循环结构展开,像是将同一个网络复制并连成一条线的结构,将自身提取的信息传递给下一个继承者,如下图所示。 这样的一条链状神经网络代表了一个递归神经网络,可以认为它是对相同神经网络的多重复制,每一时刻的神经网络会传递信息给下一时刻。

递归神经网络因为具有一定的记忆功能,可以被用来解决很多问题,例如:语音识别、语言模型、机器翻译等。但是它并不能很好地处理长时依赖问题。

利用LSTM实现预测时间序列(股票预测)(lstm输出多个预测值)

长时依赖是这样的一个问题,当预测点与依赖的相关信息距离比较远的时候,就难以学到该相关信息。例如在句子”我出生在法国,……,我会说法语“中,若要预测末尾”法语“,我们需要用到上下文”法国“。理论上,递归神经网络是可以处理这样的问题的,但是实际上,常规的递归神经网络并不能很好地解决长时依赖,好的是LSTM可以很好地解决这个问题。

3.2 LSTM网络

Long Short Term Memory networks(以下简称LSTMs),一种特殊的RNN网络,该网络设计出来是为了解决长依赖问题。该网络由 Hochreiter & Schmidhuber (1997)引入,并有许多人对其进行了改进和普及。他们的工作被用来解决了各种各样的问题,直到目前还被广泛应用。

所有循环神经网络都具有神经网络的重复模块链的形式。 在标准的RNN中,该重复模块将具有非常简单的结构,例如单个tanh层。标准的RNN网络如下图所示 LSTM也具有这种链式结构,但是它的重复单元不同于标准RNN网络里的单元只有一个网络层,它的内部有四个网络层。LSTM的结构如下图所示。 在解释LSTMs的详细结构时先定义一下图中各个符号的含义,符号包括下面几种 图中黄色类似于CNN里的激活函数操作,粉色圆圈表示点操作,单箭头表示数据流向,箭头合并表示向量的合并(concat)操作,箭头分叉表示向量的拷贝操作。

3.2.1 LSTM的核心思想

LSTM的核心是细胞状态,用贯穿细胞的水平线表示。

细胞状态像传送带一样。它贯穿整个细胞却只有很少的分支,这样能保证信息不变的流过整个RNN。细胞状态如下图所示: LSTM网络能通过一种被称为门的结构对细胞状态进行删除或者添加信息。

门能够有选择性的决定让哪些信息通过。其实门的结构很简单,就是一个sigmoid层和一个点乘操作的组合。如下图所示: 因为sigmoid层的输出是0-1的值,这代表有多少信息能够流过sigmoid层。0表示都不能通过,1表示都能通过。

一个LSTM里面包含三个门来控制细胞状态。

3.2.2 一步一步理解LSTM

前面提到LSTM由三个门来控制细胞状态,这三个门分别称为忘记门、输入门和输出门。下面一个一个的来讲述。

LSTM的第一步就是决定细胞状态需要丢弃哪些信息。这部分操作是通过一个称为忘记门的sigmoid单元来处理的。它通过查看 ht−1h_{t-1}ht−1​ 和 xtx_txt​ 信息来输出一个0-1之间的向量,该向量里面的0-1值表示细胞状态 Ct−1C_{t-1}Ct−1​中的哪些信息保留或丢弃多少。0表示不保留,1表示都保留。忘记门如下图所示: 下一步是决定给细胞状态添加哪些新的信息。这一步又分为两个步骤,首先,利用 ht−1h_{t-1}ht−1​ 和 xtx_txt​ 通过一个称为输入门的操作来决定更新哪些信息。然后利用 ht−1h_{t-1}ht−1​ 和 xtx_txt​ 通过一个tanh层得到新的候选细胞信息 Ct~\tilde{C_t}Ct​~​,这些信息可能会被更新到细胞信息中。这两步描述如下图所示: 下面将更新旧的细胞信息 Ct−1C_{t-1}Ct−1​,变为新的细胞信息 CtC_{t}Ct​。更新的规则就是通过忘记门选择忘记旧细胞信息的一部分,通过输入门选择添加候选细胞信息 C~t\tilde C_{t}C~t​ 的一部分得到新的细胞信息 CtC_{t}Ct​。更新操作如下图所示: 更新完细胞状态后需要根据输入的 ht−1h_{t-1}ht−1​ 和 xtx_txt​ 来判断输出细胞的哪些状态特征,这里需要将输入经过一个称为输出门的sigmoid层得到判断条件,然后将细胞状态经过tanh层得到一个-1~1之间值的向量,该向量与输出门得到的判断条件相乘就得到了最终该RNN单元的输出。该步骤如下图所示: 还是拿语言模型来举例说明,在预测动词形式的时候,我们需要通过输入的主语是单数还是复数来推断输出门输出的预测动词是单数形式还是复数形式。

4. 代码实现4.1 导入相关资源包

这里除了安装深度学习框架pytorch之外,还需要安装matplotlib、numpy、pandas、tushare等库,安装步骤都很简单,只需输入pip3 install xxx(库名)即可。

import matplotlib.pyplot as pltimport numpy as npimport tushare as tsimport torchfrom torch import nnimport datetimeimport time4.2 定义模型结构class LSTM_Regression(nn.Module): """ 使用LSTM进行回归 参数: - input_size: feature size - hidden_size: number of hidden units - output_size: number of output - num_layers: layers of LSTM to stack """ def __init__(self, input_size, hidden_size, output_size=1, num_layers=2): super().__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers) self.fc = nn.Linear(hidden_size, output_size) def forward(self, _x): x, _ = self.lstm(_x) # _x is input, size (seq_len, batch, input_size) s, b, h = x.shape # x is output, size (seq_len, batch, hidden_size) x = x.view(s*b, h) x = self.fc(x) x = x.view(s, b, -1) # 把形状改回来 return x4.3 制作数据集def create_dataset(data, days_for_train=5) -> (np.array, np.array): """ 根据给定的序列data,生成数据集 数据集分为输入和输出,每一个输入的长度为days_for_train,每一个输出的长度为1。 也就是说用days_for_train天的数据,对应下一天的数据。 若给定序列的长度为d,将输出长度为(d-days_for_train+1)个输入/输出对 """ dataset_x, dataset_y= [], [] for i in range(len(data)-days_for_train): _x = data[i:(i+days_for_train)] dataset_x.append(_x) dataset_y.append(data[i+days_for_train]) return (np.array(dataset_x), np.array(dataset_y))4.4 模型训练

可以根据自己的兴趣选择想要下载的时间范围,比如代码中选择了起始日期为2019年1月1日。另外训练集与测试集的长度也可以自由调节。

if __name__ == '__main__': t0 = time.time() data_close = ts.get_k_data('000001', start='2019-01-01', index=True)['close'].values # 取上证指数的收盘价的np.ndarray 而不是pd.Series data_close = data_close.astype('float32') # 转换数据类型 plt.plot(data_close) plt.savefig('data.png', format='png', dpi=200) plt.close() # 将价格标准化到0~1 max_value = np.max(data_close) min_value = np.min(data_close) data_close = (data_close - min_value) / (max_value - min_value) dataset_x, dataset_y = create_dataset(data_close, DAYS_FOR_TRAIN) # 划分训练集和测试集,70%作为训练集 train_size = int(len(dataset_x) * 0.7) train_x = dataset_x[:train_size] train_y = dataset_y[:train_size] # 将数据改变形状,RNN 读入的数据维度是 (seq_size, batch_size, feature_size) train_x = train_x.reshape(-1, 1, DAYS_FOR_TRAIN) train_y = train_y.reshape(-1, 1, 1) # 转为pytorch的tensor对象 train_x = torch.from_numpy(train_x) train_y = torch.from_numpy(train_y) model = LSTM_Regression(DAYS_FOR_TRAIN, 8, output_size=1, num_layers=2) loss_function = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=1e-2) for i in range(1000): out = model(train_x) loss = loss_function(out, train_y) loss.backward() optimizer.step() optimizer.zero_grad() with open('log.txt', 'a+') as f: f.write('{} - {}\n'.format(i+1, loss.item())) if (i+1) % 1 == 0: print('Epoch: {}, Loss:{:.5f}'.format(i+1, loss.item()))4.5 测试与保存结果 model = model.eval() # 转换成测试模式 # model.load_state_dict(torch.load('model_params.pkl')) # 读取参数 # 注意这里用的是全集 模型的输出长度会比原数据少DAYS_FOR_TRAIN 填充使长度相等再作图 dataset_x = dataset_x.reshape(-1, 1, DAYS_FOR_TRAIN) # (seq_size, batch_size, feature_size) dataset_x = torch.from_numpy(dataset_x) pred_test = model(dataset_x) # 全量训练集的模型输出 (seq_size, batch_size, output_size) pred_test = pred_test.view(-1).data.numpy() pred_test = np.concatenate((np.zeros(DAYS_FOR_TRAIN), pred_test)) # 填充0 使长度相同 assert len(pred_test) == len(data_close) plt.plot(pred_test, 'r', label='prediction') plt.plot(data_close, 'b', label='real') plt.plot((train_size, train_size), (0, 1), 'g--') # 分割线 左边是训练数据 右边是测试数据的输出 plt.legend(loc='best') plt.savefig('result.png', format='png', dpi=200) plt.close()4.6 实验结果

训练结束后在终端会有如下显示: 实验结果会保存为.png格式的图片保存在代码的根目录下,如下图所示(线条颜色可根据个人喜好自己选择)。

5. 完整代码#!/usr/bin/python3# -*- encoding: utf-8 -*-import matplotlib.pyplot as pltimport numpy as npimport tushare as tsimport pandas as pdimport torchfrom torch import nnimport datetimeimport timeDAYS_FOR_TRAIN = 10class LSTM_Regression(nn.Module): """ 使用LSTM进行回归 参数: - input_size: feature size - hidden_size: number of hidden units - output_size: number of output - num_layers: layers of LSTM to stack """ def __init__(self, input_size, hidden_size, output_size=1, num_layers=2): super().__init__() self.lstm = nn.LSTM(input_size, hidden_size, num_layers) self.fc = nn.Linear(hidden_size, output_size) def forward(self, _x): x, _ = self.lstm(_x) # _x is input, size (seq_len, batch, input_size) s, b, h = x.shape # x is output, size (seq_len, batch, hidden_size) x = x.view(s*b, h) x = self.fc(x) x = x.view(s, b, -1) # 把形状改回来 return xdef create_dataset(data, days_for_train=5) -> (np.array, np.array): """ 根据给定的序列data,生成数据集 数据集分为输入和输出,每一个输入的长度为days_for_train,每一个输出的长度为1。 也就是说用days_for_train天的数据,对应下一天的数据。 若给定序列的长度为d,将输出长度为(d-days_for_train+1)个输入/输出对 """ dataset_x, dataset_y= [], [] for i in range(len(data)-days_for_train): _x = data[i:(i+days_for_train)] dataset_x.append(_x) dataset_y.append(data[i+days_for_train]) return (np.array(dataset_x), np.array(dataset_y))if __name__ == '__main__': t0 = time.time() #data_close = ts.get_k_data('000001', start='2019-01-01', index=True)['close'] # 取上证指数的收盘价 #data_close.to_csv('000001.csv', index=False) #将下载的数据转存为.csv格式保存 data_close = pd.read_csv('000001.csv') #读取文件 #df_sh = ts.get_k_data('sh', start='2019-01-01', end=datetime.datetime.now().strftime('%Y-%m-%d')) #print(df_sh.shape) data_close = data_close.astype('float32').values # 转换数据类型 plt.plot(data_close) plt.savefig('data.png', format='png', dpi=200) plt.close() # 将价格标准化到0~1 max_value = np.max(data_close) min_value = np.min(data_close) data_close = (data_close - min_value) / (max_value - min_value) dataset_x, dataset_y = create_dataset(data_close, DAYS_FOR_TRAIN) # 划分训练集和测试集,70%作为训练集 train_size = int(len(dataset_x) * 0.7) train_x = dataset_x[:train_size] train_y = dataset_y[:train_size] # 将数据改变形状,RNN 读入的数据维度是 (seq_size, batch_size, feature_size) train_x = train_x.reshape(-1, 1, DAYS_FOR_TRAIN) train_y = train_y.reshape(-1, 1, 1) # 转为pytorch的tensor对象 train_x = torch.from_numpy(train_x) train_y = torch.from_numpy(train_y) model = LSTM_Regression(DAYS_FOR_TRAIN, 8, output_size=1, num_layers=2) # 导入模型并设置模型的参数输入输出层、隐藏层等 model_total = sum([param.nelement() for param in model.parameters()]) # 计算模型参数 print("Number of model_total parameter: %.8fM" % (model_total/1e6)) train_loss = [] loss_function = nn.MSELoss() optimizer = torch.optim.Adam(model.parameters(), lr=1e-2, betas=(0.9, 0.999), eps=1e-08, weight_decay=0) for i in range(200): out = model(train_x) loss = loss_function(out, train_y) loss.backward() optimizer.step() optimizer.zero_grad() train_loss.append(loss.item())# 将训练过程的损失值写入文档保存,并在终端打印出来 with open('log.txt', 'a+') as f: f.write('{} - {}\n'.format(i+1, loss.item())) if (i+1) % 1 == 0: print('Epoch: {}, Loss:{:.5f}'.format(i+1, loss.item())) # 画loss曲线 plt.figure() plt.plot(train_loss, 'b', label='loss') plt.title("Train_Loss_Curve") plt.ylabel('train_loss') plt.xlabel('epoch_num') plt.savefig('loss.png', format='png', dpi=200) plt.close() # torch.save(model.state_dict(), 'model_params.pkl') # 可以保存模型的参数供未来使用 t1=time.time() T=t1-t0 print('The training time took %.2f'%(T/60)+' mins.') tt0=time.asctime(time.localtime(t0)) tt1=time.asctime(time.localtime(t1)) print('The starting time was ',tt0) print('The finishing time was ',tt1) # for test model = model.eval() # 转换成测试模式 # model.load_state_dict(torch.load('model_params.pkl')) # 读取参数 # 注意这里用的是全集 模型的输出长度会比原数据少DAYS_FOR_TRAIN 填充使长度相等再作图 dataset_x = dataset_x.reshape(-1, 1, DAYS_FOR_TRAIN) # (seq_size, batch_size, feature_size) dataset_x = torch.from_numpy(dataset_x) pred_test = model(dataset_x) # 全量训练集 # 的模型输出 (seq_size, batch_size, output_size) pred_test = pred_test.view(-1).data.numpy() pred_test = np.concatenate((np.zeros(DAYS_FOR_TRAIN), pred_test)) # 填充0 使长度相同 assert len(pred_test) == len(data_close) plt.plot(pred_test, 'r', label='prediction') plt.plot(data_close, 'b', label='real') plt.plot((train_size, train_size), (0, 1), 'g--') # 分割线 左边是训练数据 右边是测试数据的输出 plt.legend(loc='best') plt.savefig('result.png', format='png', dpi=200) plt.close()
本文链接地址:https://www.jiuchutong.com/zhishi/300782.html 转载请保留说明!

上一篇:Code For Better 谷歌开发者之声——使用谷歌浏览器 Chrome 更好地调试

下一篇:ECCV2022论文列表(中英对照)(eccv论文下载)

  • beatsfitpro怎么充电(beatspro怎么充电)

    beatsfitpro怎么充电(beatspro怎么充电)

  • 人气票算主播音浪收入吗(主播人气值是什么意思)

    人气票算主播音浪收入吗(主播人气值是什么意思)

  • 支付宝好友可以视频语音聊天吗(支付宝好友可以看到我的账单吗)

    支付宝好友可以视频语音聊天吗(支付宝好友可以看到我的账单吗)

  • ipad pro充不进电怎么办(ipad pro充不进电 重启又好了)

    ipad pro充不进电怎么办(ipad pro充不进电 重启又好了)

  • 怎么关闭苹果手机美颜功能(怎么关闭苹果手机共享功能)

    怎么关闭苹果手机美颜功能(怎么关闭苹果手机共享功能)

  • 美图t9手机上市时间

    美图t9手机上市时间

  • 优酷U钻怎么充值(优酷u钻充值支付宝怎么充值)

    优酷U钻怎么充值(优酷u钻充值支付宝怎么充值)

  • 通过ip地址能否查到对方位置(通过IP地址能否查到作弊)

    通过ip地址能否查到对方位置(通过IP地址能否查到作弊)

  • 微信不能付款怎么回事(微信不能付款怎么解封)

    微信不能付款怎么回事(微信不能付款怎么解封)

  • 微信耳听模式在哪里关(微信怎么打开耳听模式)

    微信耳听模式在哪里关(微信怎么打开耳听模式)

  • 此宝贝未发布手机端什么意思(此宝贝未发布手机端详情怎么办)

    此宝贝未发布手机端什么意思(此宝贝未发布手机端详情怎么办)

  • nove5和nove5i有什么区别(华为nove5和nove5i 的区别)

    nove5和nove5i有什么区别(华为nove5和nove5i 的区别)

  • 计算机中的信息是以什么编码形式表示的(计算机中的信息都是以二进制方式表示的,主要理由是)

    计算机中的信息是以什么编码形式表示的(计算机中的信息都是以二进制方式表示的,主要理由是)

  • 怎么更改ipad序列号(ipad更改序列号是什么意思)

    怎么更改ipad序列号(ipad更改序列号是什么意思)

  • 预售订单怎么放入购物车(预售订单怎么放在桌面)

    预售订单怎么放入购物车(预售订单怎么放在桌面)

  • 拼多多怎么设置备注(拼多多怎么设置匿名评价)

    拼多多怎么设置备注(拼多多怎么设置匿名评价)

  • 网上订机票怎么取票(网上订机票怎么选座位)

    网上订机票怎么取票(网上订机票怎么选座位)

  • 苹果手机怎么连蓝牙耳机(苹果手机怎么连接车载carplay)

    苹果手机怎么连蓝牙耳机(苹果手机怎么连接车载carplay)

  • 怎么查拼多多个人消费(怎么查拼多多个人店铺信息)

    怎么查拼多多个人消费(怎么查拼多多个人店铺信息)

  • window7截图工具在哪(win7截图快捷键)

    window7截图工具在哪(win7截图快捷键)

  • ios13修改音量条(iphone13音量条)

    ios13修改音量条(iphone13音量条)

  • 一加七pro和一加七区别(一加七pro和一加七t区别)

    一加七pro和一加七区别(一加七pro和一加七t区别)

  • 在BIOS中关闭软驱的方法(禁用软驱)(bios中关闭软盘)

    在BIOS中关闭软驱的方法(禁用软驱)(bios中关闭软盘)

  • 文件类型可以通过什么来识别详情(文件的类型可以通过什么来区分)

    文件类型可以通过什么来识别详情(文件的类型可以通过什么来区分)

  • flex布局中使用flex-wrap实现换行(flex布局教程实例篇)

    flex布局中使用flex-wrap实现换行(flex布局教程实例篇)

  • 个税申报怎么办
  • 有关研发支出的账务怎么做?
  • 公司固定资产抵税
  • 差额增税可以抵扣吗
  • 咨询公司所得税税负率是多少合适
  • 金融商品转让为什么不能扣除相关税费
  • 岗位津贴和岗位补贴
  • 公司向个人租房子怎么做账
  • 应付股东款可以结转实收资本吗
  • 公户收到的款都要确定收入吗
  • 个人所得税手续费
  • 电子发票怎么缩小比例
  • 城市垃圾处理费怎么申报
  • 允许列支的捐赠比例
  • 药用植物所
  • 个人抬头的医药费可以进费用吗
  • 进项税转出月底怎么处理
  • 现金支票怎么背书转让
  • 购货申请书
  • 未完工产品成本计算公式
  • 工程物资期末余额应在资产负债表中填列?
  • windows10最新激活密钥2020
  • 库存现金银行存款用什么凭证
  • 清算所得税申报表范本
  • 清除cookies有什么用
  • 产品销售账务处理办法
  • PHP:http_build_query()的用法_url函数
  • 以前年度多计提的工资怎么处理
  • powershell.exe什么意思
  • jusched.exe
  • 语音模块作用
  • 应交税金的会计科目
  • 豁免债权如何做账
  • 企业投资固定资产
  • yolov5实例分割原理详解
  • 企业固定资产可以按照其价值和使用情况,确定采用某一
  • 计提坏账的比例是多少
  • java使用循环结构输出九九乘法表
  • python按行读入
  • 中国烟草资产负债表
  • Huggingface之transformers零基础使用指南
  • 在岗职工平均工资和社平工资的区别
  • c语言中有哪些语句
  • poi java 导入导出
  • mysql的左右连接
  • 劳务公司账务处理实例
  • 投资性房地产累计摊销
  • 劳务派遣工资可以税前扣除吗
  • 工资不计提直接做管理费用可以吗
  • 职工教育经费超过扣除限额的时候调增还是调减
  • 余利宝本金会亏吗
  • 老板垫付货款
  • 公司库存现金提现流程
  • 开空头发票犯法吗
  • 公司增资怎么处理
  • 售后回租含手续费吗
  • 投资收益余额在贷方怎么结转
  • 出差开会总结怎么写
  • 应付账款的贷方等于预收账款的借方
  • sql server常规错误
  • mysql coalesce函数用法
  • Win10预览版拆弹
  • 硬盘安装64位win8.1/win8或win7操作系统图文教程
  • securecrt设置英文
  • 升级win10免费吗
  • win7如何清理c盘空间不影响系统
  • node 线程 进程
  • js数据的基本类型有哪些
  • python常见算法
  • gin项目
  • javascript判断
  • android 加载大图
  • python的cumprod
  • Python出现keyerror
  • 云阅卷平台登录入口云南省
  • ipadpro关税多少
  • 山东省地方税务局历任局长
  • 房屋赠与给子女要交多少税
  • 开展培训举办培训
  • 税收分类编码1080499
  • 免责声明:网站部分图片文字素材来源于网络,如有侵权,请及时告知,我们会第一时间删除,谢谢! 邮箱:opceo@qq.com

    鄂ICP备2023003026号

    网站地图: 企业信息 工商信息 财税知识 网络常识 编程技术

    友情链接: 武汉网站建设