当前位置: 首页 > news >正文

成都网站建设电话北京海淀工商局网站

成都网站建设电话,北京海淀工商局网站,wordpress新建文章页修改图片样式,网站什么引导页RNN实现情感分类 概述 情感分类是自然语言处理中的经典任务#xff0c;是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型#xff0c;实现如下的效果#xff1a; 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This fil…RNN实现情感分类 概述 情感分类是自然语言处理中的经典任务是典型的分类问题。本节使用MindSpore实现一个基于RNN网络的情感分类模型实现如下的效果 输入: This film is terrible 正确标签: Negative 预测标签: Negative输入: This film is great 正确标签: Positive 预测标签: Positive数据准备 本节使用情感分类的经典数据集IMDB影评数据集数据集包含Positive和Negative两类下面为其样例 ReviewLabel“Quitting” may be as much about exiting a pre-ordained identity as about drug withdrawal. As a rural guy coming to Beijing, class and success must have struck this young artist face on as an appeal to separate from his roots and far surpass his peasant parents’ acting success. Troubles arise, however, when the new man is too new, when it demands too big a departure from family, history, nature, and personal identity. The ensuing splits, and confusion between the imaginary and the real and the dissonance between the ordinary and the heroic are the stuff of a gut check on the one hand or a complete escape from self on the other.NegativeThis movie is amazing because the fact that the real people portray themselves and their real life experience and do such a good job it’s like they’re almost living the past over again. Jia Hongsheng plays himself an actor who quit everything except music and drugs struggling with depression and searching for the meaning of life while being angry at everyone especially the people who care for him most.Positive 此外需要使用预训练词向量对自然语言单词进行编码以获取文本的语义特征本节选取Glove词向量作为Embedding。 数据下载模块 为了方便数据集和预训练词向量的下载首先设计数据下载模块实现可视化下载流程并保存至指定路径。数据下载模块使用requests库进行http请求并通过tqdm库对下载百分比进行可视化。此外针对下载安全性使用IO的方式下载临时文件而后保存至指定的路径并返回。 tqdm和requests库需手动安装命令如下pip install tqdm requests %%capture captured_output # 实验环境已经预装了mindspore2.2.14如需更换mindspore版本可更改下面mindspore的版本号 # !pip uninstall mindspore -y # !pip install -i https://pypi.mirrors.ustc.edu.cn/simple mindspore2.2.14# 查看当前 mindspore 版本 !pip show mindsporeName: mindspore Version: 2.2.14 Summary: MindSpore is a new open source deep learning training/inference framework that could be used for mobile, edge and cloud scenarios. Home-page: https://www.mindspore.cn Author: The MindSpore Authors Author-email: contactmindspore.cn License: Apache 2.0 Location: /home/nginx/miniconda/envs/jupyter/lib/python3.9/site-packages Requires: asttokens, astunparse, numpy, packaging, pillow, protobuf, psutil, scipy Required-by: import os import shutil import requests import tempfile from tqdm import tqdm from typing import IO from pathlib import Path# 指定保存路径为 home_path/.mindspore_examples cache_dir Path.home() / .mindspore_examplesdef http_get(url: str, temp_file: IO):使用requests库下载数据并使用tqdm库进行流程可视化req requests.get(url, streamTrue)content_length req.headers.get(Content-Length)total int(content_length) if content_length is not None else Noneprogress tqdm(unitB, totaltotal)for chunk in req.iter_content(chunk_size1024):if chunk:progress.update(len(chunk))temp_file.write(chunk)progress.close()def download(file_name: str, url: str):下载数据并存为指定名称if not os.path.exists(cache_dir):os.makedirs(cache_dir)cache_path os.path.join(cache_dir, file_name)cache_exist os.path.exists(cache_path)if not cache_exist:with tempfile.NamedTemporaryFile() as temp_file:http_get(url, temp_file)temp_file.flush()temp_file.seek(0)with open(cache_path, wb) as cache_file:shutil.copyfileobj(temp_file, cache_file)return cache_path完成数据下载模块后下载IMDB数据集进行测试(此处使用华为云的镜像用于提升下载速度)。下载过程及保存的路径如下 imdb_path download(aclImdb_v1.tar.gz, https://mindspore-website.obs.myhuaweicloud.com/notebook/datasets/aclImdb_v1.tar.gz) imdb_path/home/nginx/.mindspore_examples/aclImdb_v1.tar.gz加载IMDB数据集 下载好的IMDB数据集为tar.gz文件我们使用Python的tarfile库对其进行读取并将所有数据和标签分别进行存放。原始的IMDB数据集解压目录如下 ├── aclImdb│ ├── imdbEr.txt│ ├── imdb.vocab│ ├── README│ ├── test│ └── train│ ├── neg│ ├── pos...数据集已分割为train和test两部分且每部分包含neg和pos两个分类的文件夹因此需分别train和test进行读取并处理数据和标签。 import re import six import string import tarfileclass IMDBData():IMDB数据集加载器加载IMDB数据集并处理为一个Python迭代对象。label_map {pos: 1,neg: 0}def __init__(self, path, modetrain):self.mode modeself.path pathself.docs, self.labels [], []self._load(pos)self._load(neg)def _load(self, label):pattern re.compile(raclImdb/{}/{}/.*\.txt$.format(self.mode, label))# 将数据加载至内存with tarfile.open(self.path) as tarf:tf tarf.next()while tf is not None:if bool(pattern.match(tf.name)):# 对文本进行分词、去除标点和特殊字符、小写处理self.docs.append(str(tarf.extractfile(tf).read().rstrip(six.b(\n\r)).translate(None, six.b(string.punctuation)).lower()).split())self.labels.append([self.label_map[label]])tf tarf.next()def __getitem__(self, idx):return self.docs[idx], self.labels[idx]def __len__(self):return len(self.docs)完成IMDB数据加载器后加载训练数据集进行测试输出数据集数量 imdb_train IMDBData(imdb_path, train) len(imdb_train)25000将IMDB数据集加载至内存并构造为迭代对象后可以使用mindspore.dataset提供的Generatordataset接口加载数据集迭代对象并进行下一步的数据处理下面封装一个函数将train和test分别使用Generatordataset进行加载并指定数据集中文本和标签的column_name分别为text和label: import mindspore.dataset as dsdef load_imdb(imdb_path):imdb_train ds.GeneratorDataset(IMDBData(imdb_path, train), column_names[text, label], shuffleTrue, num_samples10000)imdb_test ds.GeneratorDataset(IMDBData(imdb_path, test), column_names[text, label], shuffleFalse)return imdb_train, imdb_test加载IMDB数据集可以看到imdb_train是一个GeneratorDataset对象。 imdb_train, imdb_test load_imdb(imdb_path) imdb_trainmindspore.dataset.engine.datasets_user_defined.GeneratorDataset at 0xffff8c3d6310加载预训练词向量 预训练词向量是对输入单词的数值化表示通过nn.Embedding层采用查表的方式输入单词对应词表中的index获得对应的表达向量。 因此进行模型构造前需要将Embedding层所需的词向量和词表进行构造。这里我们使用Glove(Global Vectors for Word Representation)这种经典的预训练词向量 其数据格式如下 WordVectorthe0.418 0.24968 -0.41242 0.1217 0.34527 -0.044457 -0.49688 -0.17862 -0.00066023 …,0.013441 0.23682 -0.16899 0.40951 0.63812 0.47709 -0.42852 -0.55641 -0.364 … 我们直接使用第一列的单词作为词表使用dataset.text.Vocab将其按顺序加载同时读取每一行的Vector并转为numpy.array用于nn.Embedding加载权重使用。具体实现如下 import zipfile import numpy as npdef load_glove(glove_path):glove_100d_path os.path.join(cache_dir, glove.6B.100d.txt)if not os.path.exists(glove_100d_path):glove_zip zipfile.ZipFile(glove_path)glove_zip.extractall(cache_dir)embeddings []tokens []with open(glove_100d_path, encodingutf-8) as gf:for glove in gf:word, embedding glove.split(maxsplit1)tokens.append(word)embeddings.append(np.fromstring(embedding, dtypenp.float32, sep ))# 添加 unk, pad 两个特殊占位符对应的embeddingembeddings.append(np.random.rand(100))embeddings.append(np.zeros((100,), np.float32))vocab ds.text.Vocab.from_list(tokens, special_tokens[unk, pad], special_firstFalse)embeddings np.array(embeddings).astype(np.float32)return vocab, embeddings由于数据集中可能存在词表没有覆盖的单词因此需要加入unk标记符同时由于输入长度的不一致在打包为一个batch时需要将短的文本进行填充因此需要加入pad标记符。完成后的词表长度为原词表长度2。 下面下载Glove词向量并加载生成词表和词向量权重矩阵。 glove_path download(glove.6B.zip, https://mindspore-website.obs.myhuaweicloud.com/notebook/datasets/glove.6B.zip) vocab, embeddings load_glove(glove_path) len(vocab.vocab())400002使用词表将the转换为index id并查询词向量矩阵对应的词向量 idx vocab.tokens_to_ids(the) embedding embeddings[idx] idx, embedding(0,array([-0.038194, -0.24487 , 0.72812 , -0.39961 , 0.083172, 0.043953,-0.39141 , 0.3344 , -0.57545 , 0.087459, 0.28787 , -0.06731 ,0.30906 , -0.26384 , -0.13231 , -0.20757 , 0.33395 , -0.33848 ,-0.31743 , -0.48336 , 0.1464 , -0.37304 , 0.34577 , 0.052041,0.44946 , -0.46971 , 0.02628 , -0.54155 , -0.15518 , -0.14107 ,-0.039722, 0.28277 , 0.14393 , 0.23464 , -0.31021 , 0.086173,0.20397 , 0.52624 , 0.17164 , -0.082378, -0.71787 , -0.41531 ,0.20335 , -0.12763 , 0.41367 , 0.55187 , 0.57908 , -0.33477 ,-0.36559 , -0.54857 , -0.062892, 0.26584 , 0.30205 , 0.99775 ,-0.80481 , -3.0243 , 0.01254 , -0.36942 , 2.2167 , 0.72201 ,-0.24978 , 0.92136 , 0.034514, 0.46745 , 1.1079 , -0.19358 ,-0.074575, 0.23353 , -0.052062, -0.22044 , 0.057162, -0.15806 ,-0.30798 , -0.41625 , 0.37972 , 0.15006 , -0.53212 , -0.2055 ,-1.2526 , 0.071624, 0.70565 , 0.49744 , -0.42063 , 0.26148 ,-1.538 , -0.30223 , -0.073438, -0.28312 , 0.37104 , -0.25217 ,0.016215, -0.017099, -0.38984 , 0.87424 , -0.72569 , -0.51058 ,-0.52028 , -0.1459 , 0.8278 , 0.27062 ], dtypefloat32))数据集预处理 通过加载器加载的IMDB数据集进行了分词处理但不满足构造训练数据的需要因此要对其进行额外的预处理。其中包含的预处理如下 通过Vocab将所有的Token处理为index id。将文本序列统一长度不足的使用pad补齐超出的进行截断。 这里我们使用mindspore.dataset中提供的接口进行预处理操作。这里使用到的接口均为MindSpore的高性能数据引擎设计每个接口对应操作视作数据流水线的一部分详情请参考MindSpore数据引擎。 首先针对token到index id的查表操作使用text.Lookup接口将前文构造的词表加载并指定unknown_token。其次为文本序列统一长度操作使用PadEnd接口此接口定义最大长度和补齐值(pad_value)这里我们取最大长度为500填充值对应词表中pad的index id。 除了对数据集中text进行预处理外由于后续模型训练的需要要将label数据转为float32格式。 import mindspore as mslookup_op ds.text.Lookup(vocab, unknown_tokenunk) pad_op ds.transforms.PadEnd([500], pad_valuevocab.tokens_to_ids(pad)) type_cast_op ds.transforms.TypeCast(ms.float32)完成预处理操作后需将其加入到数据集处理流水线中使用map接口对指定的column添加操作。 imdb_train imdb_train.map(operations[lookup_op, pad_op], input_columns[text]) imdb_train imdb_train.map(operations[type_cast_op], input_columns[label])imdb_test imdb_test.map(operations[lookup_op, pad_op], input_columns[text]) imdb_test imdb_test.map(operations[type_cast_op], input_columns[label])由于IMDB数据集本身不包含验证集我们手动将其分割为训练和验证两部分比例取0.7, 0.3。 imdb_train, imdb_valid imdb_train.split([0.7, 0.3])[WARNING] ME(49995:281473341991216,MainProcess):2024-07-05-10:40:25.898.932 [mindspore/dataset/engine/datasets.py:1203] Dataset is shuffled before split.最后指定数据集的batch大小通过batch接口指定并设置是否丢弃无法被batch size整除的剩余数据。 调用数据集的map、split、batch为数据集处理流水线增加对应操作返回值为新的Dataset类型。现在仅定义流水线操作在执行时开始执行数据处理流水线获取最终处理好的数据并送入模型进行训练。 imdb_train imdb_train.batch(64, drop_remainderTrue) imdb_valid imdb_valid.batch(64, drop_remainderTrue)模型构建 完成数据集的处理后我们设计用于情感分类的模型结构。首先需要将输入文本(即序列化后的index id列表)通过查表转为向量化表示此时需要使用nn.Embedding层加载Glove词向量然后使用RNN循环神经网络做特征提取最后将RNN连接至一个全连接层即nn.Dense将特征转化为与分类数量相同的size用于后续进行模型优化训练。整体模型结构如下 nn.Embedding - nn.RNN - nn.Dense这里我们使用能够一定程度规避RNN梯度消失问题的变种LSTM(Long short-term memory)做特征提取层。下面对模型进行详解 Embedding Embedding层又可称为EmbeddingLookup层其作用是使用index id对权重矩阵对应id的向量进行查找当输入为一个由index id组成的序列时则查找并返回一个相同长度的矩阵例如 embedding nn.Embedding(1000, 100) # 词表大小(index的取值范围)为1000表示向量的size为100 input shape: (1, 16) # 序列长度为16 output shape: (1, 16, 100)这里我们使用前文处理好的Glove词向量矩阵设置nn.Embedding的embedding_table为预训练词向量矩阵。对应的vocab_size为词表大小400002embedding_size为选用的glove.6B.100d向量大小即100。 RNN(循环神经网络) 循环神经网络Recurrent Neural Network, RNN是一类以序列sequence数据为输入在序列的演进方向进行递归recursion且所有节点循环单元按链式连接的神经网络。下图为RNN的一般结构 图示左侧为一个RNN Cell循环右侧为RNN的链式连接平铺。实际上不管是单个RNN Cell还是一个RNN网络都只有一个Cell的参数在不断进行循环计算中更新。 由于RNN的循环特性和自然语言文本的序列特性(句子是由单词组成的序列)十分匹配因此被大量应用于自然语言处理研究中。下图为RNN的结构拆解 RNN单个Cell的结构简单因此也造成了梯度消失(Gradient Vanishing)问题具体表现为RNN网络在序列较长时在序列尾部已经基本丢失了序列首部的信息。为了克服这一问题LSTM(Long short-term memory)被提出通过门控机制(Gating Mechanism)来控制信息流在每个循环步中的留存和丢弃。下图为LSTM的结构拆解 本节我们选择LSTM变种而不是经典的RNN做特征提取来规避梯度消失问题并获得更好的模型效果。下面来看MindSpore中nn.LSTM对应的公式 h 0 : t , ( h t , c t ) LSTM ( x 0 : t , ( h 0 , c 0 ) ) h_{0:t}, (h_t, c_t) \text{LSTM}(x_{0:t}, (h_0, c_0)) h0:t​,(ht​,ct​)LSTM(x0:t​,(h0​,c0​)) 这里nn.LSTM隐藏了整个循环神经网络在序列时间步(Time step)上的循环送入输入序列、初始状态即可获得每个时间步的隐状态(hidden state)拼接而成的矩阵以及最后一个时间步对应的隐状态。我们使用最后的一个时间步的隐状态作为输入句子的编码特征送入下一层。 Time step在循环神经网络计算的每一次循环成为一个Time step。在送入文本序列时一个Time step对应一个单词。因此在本例中LSTM的输出 h 0 : t h_{0:t} h0:t​对应每个单词的隐状态集合 h t h_t ht​和 c t c_t ct​对应最后一个单词对应的隐状态。 Dense 在经过LSTM编码获取句子特征后将其送入一个全连接层即nn.Dense将特征维度变换为二分类所需的维度1经过Dense层后的输出即为模型预测结果。 import math import mindspore as ms import mindspore.nn as nn import mindspore.ops as ops from mindspore.common.initializer import Uniform, HeUniformclass RNN(nn.Cell):def __init__(self, embeddings, hidden_dim, output_dim, n_layers,bidirectional, pad_idx):super().__init__()vocab_size, embedding_dim embeddings.shapeself.embedding nn.Embedding(vocab_size, embedding_dim, embedding_tablems.Tensor(embeddings), padding_idxpad_idx)self.rnn nn.LSTM(embedding_dim,hidden_dim,num_layersn_layers,bidirectionalbidirectional,batch_firstTrue)weight_init HeUniform(math.sqrt(5))bias_init Uniform(1 / math.sqrt(hidden_dim * 2))self.fc nn.Dense(hidden_dim * 2, output_dim, weight_initweight_init, bias_initbias_init)def construct(self, inputs):embedded self.embedding(inputs)_, (hidden, _) self.rnn(embedded)hidden ops.concat((hidden[-2, :, :], hidden[-1, :, :]), axis1)output self.fc(hidden)return output损失函数与优化器 完成模型主体构建后首先根据指定的参数实例化网络然后选择损失函数和优化器。针对本节情感分类问题的特性即预测Positive或Negative的二分类问题我们选择nn.BCEWithLogitsLoss(二分类交叉熵损失函数)。 hidden_size 256 output_size 1 num_layers 2 bidirectional True lr 0.001 pad_idx vocab.tokens_to_ids(pad)model RNN(embeddings, hidden_size, output_size, num_layers, bidirectional, pad_idx) loss_fn nn.BCEWithLogitsLoss(reductionmean) optimizer nn.Adam(model.trainable_params(), learning_ratelr)训练逻辑 在完成模型构建进行训练逻辑的设计。一般训练逻辑分为一下步骤 读取一个Batch的数据送入网络进行正向计算和反向传播更新权重返回loss。 下面按照此逻辑使用tqdm库设计训练一个epoch的函数用于训练过程和loss的可视化。 def forward_fn(data, label):logits model(data)loss loss_fn(logits, label)return lossgrad_fn ms.value_and_grad(forward_fn, None, optimizer.parameters)def train_step(data, label):loss, grads grad_fn(data, label)optimizer(grads)return lossdef train_one_epoch(model, train_dataset, epoch0):model.set_train()total train_dataset.get_dataset_size()loss_total 0step_total 0with tqdm(totaltotal) as t:t.set_description(Epoch %i % epoch)for i in train_dataset.create_tuple_iterator():loss train_step(*i)loss_total loss.asnumpy()step_total 1t.set_postfix(lossloss_total/step_total)t.update(1)评估指标和逻辑 训练逻辑完成后需要对模型进行评估。即使用模型的预测结果和测试集的正确标签进行对比求出预测的准确率。由于IMDB的情感分类为二分类问题对预测值直接进行四舍五入即可获得分类标签(0或1)然后判断是否与正确标签相等即可。下面为二分类准确率计算函数实现 def binary_accuracy(preds, y):计算每个batch的准确率# 对预测值进行四舍五入rounded_preds np.around(ops.sigmoid(preds).asnumpy())correct (rounded_preds y).astype(np.float32)acc correct.sum() / len(correct)return acc有了准确率计算函数后类似于训练逻辑对评估逻辑进行设计, 分别为以下步骤 读取一个Batch的数据送入网络进行正向计算获得预测结果计算准确率。 同训练逻辑一样使用tqdm进行loss和过程的可视化。此外返回评估loss至供保存模型时作为模型优劣的判断依据。 在进行evaluate时使用的模型是不包含损失函数和优化器的网络主体 在进行evaluate前需要通过model.set_train(False)将模型置为评估状态此时Dropout不生效。 def evaluate(model, test_dataset, criterion, epoch0):total test_dataset.get_dataset_size()epoch_loss 0epoch_acc 0step_total 0model.set_train(False)with tqdm(totaltotal) as t:t.set_description(Epoch %i % epoch)for i in test_dataset.create_tuple_iterator():predictions model(i[0])loss criterion(predictions, i[1])epoch_loss loss.asnumpy()acc binary_accuracy(predictions, i[1])epoch_acc accstep_total 1t.set_postfix(lossepoch_loss/step_total, accepoch_acc/step_total)t.update(1)return epoch_loss / total模型训练与保存 前序完成了模型构建和训练、评估逻辑的设计下面进行模型训练。这里我们设置训练轮数为5轮。同时维护一个用于保存最优模型的变量best_valid_loss根据每一轮评估的loss值取loss值最小的轮次将模型进行保存。为节省用例运行时长此处num_epochs设置为2可根据需要自行修改。 num_epochs 5 best_valid_loss float(inf) ckpt_file_name os.path.join(cache_dir, sentiment-analysis.ckpt)for epoch in range(num_epochs):train_one_epoch(model, imdb_train, epoch)valid_loss evaluate(model, imdb_valid, loss_fn, epoch)if valid_loss best_valid_loss:best_valid_loss valid_lossms.save_checkpoint(model, ckpt_file_name)Epoch 0: 0%| | 0/109 [00:00?, ?it/s]-Epoch 0: 100%|██████████| 109/109 [10:1300:00, 5.63s/it, loss0.673] Epoch 0: 100%|██████████| 46/46 [00:2300:00, 1.95it/s, acc0.652, loss0.626] Epoch 1: 100%|██████████| 109/109 [01:2700:00, 1.24it/s, loss0.67] Epoch 1: 100%|██████████| 46/46 [00:1400:00, 3.25it/s, acc0.653, loss0.633] Epoch 2: 100%|██████████| 109/109 [01:3000:00, 1.20it/s, loss0.612] Epoch 2: 100%|██████████| 46/46 [00:1300:00, 3.33it/s, acc0.74, loss0.543] Epoch 3: 100%|██████████| 109/109 [01:2900:00, 1.22it/s, loss0.559] Epoch 3: 100%|██████████| 46/46 [00:1300:00, 3.37it/s, acc0.749, loss0.529] Epoch 4: 100%|██████████| 109/109 [01:2900:00, 1.22it/s, loss0.518] Epoch 4: 100%|██████████| 46/46 [00:1300:00, 3.36it/s, acc0.751, loss0.523]可以看到每轮Loss逐步下降在验证集上的准确率逐步提升。 模型加载与测试 模型训练完成后一般需要对模型进行测试或部署上线此时需要加载已保存的最优模型(即checkpoint)供后续测试使用。这里我们直接使用MindSpore提供的Checkpoint加载和网络权重加载接口1.将保存的模型Checkpoint加载到内存中2.将Checkpoint加载至模型。 load_param_into_net接口会返回模型中没有和Checkpoint匹配的权重名正确匹配时返回空列表。 param_dict ms.load_checkpoint(ckpt_file_name) ms.load_param_into_net(model, param_dict)([], [])对测试集打batch然后使用evaluate方法进行评估得到模型在测试集上的效果。 imdb_test imdb_test.batch(64) evaluate(model, imdb_test, loss_fn)Epoch 0: 100%|█████████▉| 390/391 [01:2900:00, 4.56it/s, acc0.696, loss0.575]\\ Epoch 0: 100%|██████████| 391/391 [01:4000:00, 3.88it/s, acc0.696, loss0.575]0.5750424911451462自定义输入测试 最后我们设计一个预测函数实现开头描述的效果输入一句评价获得评价的情感分类。具体包含以下步骤: 将输入句子进行分词使用词表获取对应的index id序列index id序列转为Tensor送入模型获得预测结果打印输出预测结果。 具体实现如下 score_map {1: Positive,0: Negative }def predict_sentiment(model, vocab, sentence):model.set_train(False)tokenized sentence.lower().split()indexed vocab.tokens_to_ids(tokenized)tensor ms.Tensor(indexed, ms.int32)tensor tensor.expand_dims(0)prediction model(tensor)return score_map[int(np.round(ops.sigmoid(prediction).asnumpy()))]最后我们预测开头的样例可以看到模型可以很好地将评价语句的情感进行分类。 predict_sentiment(model, vocab, This film is terrible)Negativepredict_sentiment(model, vocab, This film is great)Positive
http://www.zqtcl.cn/news/292512/

相关文章:

  • 南京网站建设黄页网站大全在线看免费
  • 网站开发过程记录定制西服
  • 长沙有哪些网站建设公司用vue做网站的实例
  • 购物网站模板 php网页设计图片剧中
  • 嘉峪关市建设局网站横沥镇网站建设公司
  • 查找网站软件培训记录
  • 注册网站安全吗制作人漫画
  • jeecms 怎么建设网站嵌入式软件开发兼职
  • 百度怎么自己做网站四川省建设工程交易中心网站
  • 中介如何做网站收客wordpress 运行代码
  • 网页设计与网站建设考试题目如何做建议的网站
  • 网站怎么推广软文政务网站建设存在问题
  • 公司官方网站建设需要多少钱wordpress单页面制作
  • 社群网站建设网站推广的方式有哪几种
  • 培训机构活动策划网站wordpress 改端口
  • 北京网站制作与营销培训用asp做网站视频
  • 江苏丹阳建设公司网站做网站中的剪辑图片
  • 纯静态网站怎样广州工程造价信息网
  • 为什么网页不能打开建设银行网站企业网站开发综合实训
  • 企业网站制作 深圳网站建站行业公司主页建设
  • 外汇直播网站建设开发做网站空间商需要办什么手续
  • 源码哥网站的模板皮肤病在线咨询医生免费咨询
  • 温岭市市住房和城乡建设规划局网站附近的电脑培训班在哪里
  • 网站备案百度站长提交减肥网站源码
  • 网站添加文章机械代加工厂家
  • 学做各种糕点的网站cn网站建设多少钱
  • 首页网站关键词优化教程如何查询网站点击率
  • 文章类型的网站模版北京朝阳区房价2023年最新房价
  • wap网站发布注销主体和注销网站
  • 微信小程序 做网站满足客户的分销管理系统