1 大纲概述

  文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类。总共有以下系列:

  word2vec预训练词向量

  textCNN 模型

  charCNN 模型

  Bi-LSTM 模型

  Bi-LSTM + Attention 模型

  RCNN 模型

  Adversarial LSTM 模型

  Transformer 模型

  ELMo 预训练模型

  BERT 预训练模型

  所有代码均在textClassifier仓库中。

2 数据集

  数据集为IMDB 电影影评,总共有三个数据文件,在/data/rawData目录下,包括unlabeledTrainData.tsv,labeledTrainData.tsv,testData.tsv。在进行文本分类时需要有标签的数据(labeledTrainData),数据预处理如文本分类实战(一)—— word2vec预训练词向量中一样,预处理后的文件为/data/preprocess/labeledTrain.csv。

3 ELMO 预训练模型

  ELMo模型是利用BiLM(双向语言模型)来预训练词的向量表示,可以根据我们的训练集动态的生成词的向量表示。ELMo预训练模型来源于论文:Deep contextualized word representations。具体的ELMo模型的详细介绍见ELMO模型(Deep contextualized word representation)。

  ELMo的模型代码发布在github上,我们在调用ELMo预训练模型时主要使用到bilm中的代码,因此可以将bilm这个文件夹拷贝到自己的项目路径下,之后需要导入这个文件夹中的类和函数。此外,usage_cached.py,usage_character.py,usage_token.py这三个文件中的代码是告诉你该怎么去调用ELMo模型动态的生成词向量。在这里我们使用usage_token.py中的方法,这个计算量相对要小一些。

  在使用之前我们还需要去下载已经预训练好的模型参数权重,打开https://allennlp.org/elmo链接,在Pre-trained ELMo Models 这个版块下总共有四个不同版本的模型,可以自己选择,我们在这里选择Small这个规格的模型,总共有两个文件需要下载,一个"options"的json文件,保存了模型的配置参数,另一个是"weights"的hdf5文件,保存了模型的结构和权重值(可以用h5py读取看看)。

4 配置参数

  在这里我们需要将optionFile,vocabFile,weightsFile,tokenEmbeddingFile的路径配置上,还有一个需要注意的地方就是这里的embeddingSize的值要和ELMo的词向量的大小一致

  我们需要导入bilm文件夹中的函数和类

import os
import csv
import time
import datetime
import random from collections import Counter
from math import sqrt import pandas as pd
import numpy as np
import tensorflow as tf
from sklearn.metrics import roc_auc_score, accuracy_score, precision_score, recall_score from bilm import TokenBatcher, BidirectionalLanguageModel, weight_layers, dump_token_embeddings, Batcher
# 配置参数

class TrainingConfig(object):
epoches =
evaluateEvery =
checkpointEvery =
learningRate = 0.001 class ModelConfig(object):
embeddingSize = # 这个值是和ELMo模型的output Size 对应的值 hiddenSizes = [] # LSTM结构的神经元个数 dropoutKeepProb = 0.5
l2RegLambda = 0.0 class Config(object):
sequenceLength = # 取了所有序列长度的均值
batchSize = dataSource = "../data/preProcess/labeledTrain.csv" stopWordSource = "../data/english" optionFile = "modelParams/elmo_options.json"
weightFile = "modelParams/elmo_weights.hdf5"
vocabFile = "modelParams/vocab.txt"
tokenEmbeddingFile = 'modelParams/elmo_token_embeddings.hdf5' numClasses = rate = 0.8 # 训练集的比例 training = TrainingConfig() model = ModelConfig() # 实例化配置参数对象
config = Config()

5 数据预处理

  1)将数据读取出来,

  2)根据训练集生成vocabFile文件,

  3)调用bilm文件夹中的dump_token_embeddings方法生成初始化的词向量表示,并保存为hdf5文件,文件中的键为"embedding",

  4)固定输入数据的序列长度

  5)分割成训练集和测试集

# 数据预处理的类,生成训练集和测试集

class Dataset(object):
def __init__(self, config):
self._dataSource = config.dataSource
self._stopWordSource = config.stopWordSource
self._optionFile = config.optionFile
self._weightFile = config.weightFile
self._vocabFile = config.vocabFile
self._tokenEmbeddingFile = config.tokenEmbeddingFile self._sequenceLength = config.sequenceLength # 每条输入的序列处理为定长
self._embeddingSize = config.model.embeddingSize
self._batchSize = config.batchSize
self._rate = config.rate self.trainReviews = []
self.trainLabels = [] self.evalReviews = []
self.evalLabels = [] def _readData(self, filePath):
"""
从csv文件中读取数据集
""" df = pd.read_csv(filePath)
labels = df["sentiment"].tolist()
review = df["review"].tolist()
reviews = [line.strip().split() for line in review] return reviews, labels def _genVocabFile(self, reviews):
"""
用我们的训练数据生成一个词汇文件,并加入三个特殊字符
"""
allWords = [word for review in reviews for word in review]
wordCount = Counter(allWords) # 统计词频
sortWordCount = sorted(wordCount.items(), key=lambda x: x[1], reverse=True)
words = [item[0] for item in sortWordCount.items()]
allTokens = ['<S>', '</S>', '<UNK>'] + words
with open(self._vocabFile, 'w') as fout:
fout.write('\n'.join(allTokens)) def _fixedSeq(self, reviews):
"""
将长度超过200的截断为200的长度
"""
return [review[:self._sequenceLength] for review in reviews] def _genElmoEmbedding(self):
"""
调用ELMO源码中的dump_token_embeddings方法,基于字符的表示生成词的向量表示。并保存成hdf5文件,文件中的"embedding"键对应的value就是
词汇表文件中各词汇的向量表示,这些词汇的向量表示之后会作为BiLM的初始化输入。
"""
dump_token_embeddings(
self._vocabFile, self._optionFile, self._weightFile, self._tokenEmbeddingFile) def _genTrainEvalData(self, x, y, rate):
"""
生成训练集和验证集
"""
y = [[item] for item in y]
trainIndex = int(len(x) * rate) trainReviews = x[:trainIndex]
trainLabels = y[:trainIndex] evalReviews = x[trainIndex:]
evalLabels = y[trainIndex:] return trainReviews, trainLabels, evalReviews, evalLabels def dataGen(self):
"""
初始化训练集和验证集
""" # 初始化数据集
reviews, labels = self._readData(self._dataSource) # self._genVocabFile(reviews) # 生成vocabFile
# self._genElmoEmbedding() # 生成elmo_token_embedding reviews = self._fixedSeq(reviews) # 初始化训练集和测试集
trainReviews, trainLabels, evalReviews, evalLabels = self._genTrainEvalData(reviews, labels, self._rate)
self.trainReviews = trainReviews
self.trainLabels = trainLabels self.evalReviews = evalReviews
self.evalLabels = evalLabels data = Dataset(config)
data.dataGen()

6 batch数据生成

# 输出batch数据集
def nextBatch(x, y, batchSize):
"""
生成batch数据集,用生成器的方式输出
"""
# 每一个epoch时,都要打乱数据集
midVal = list(zip(x, y))
random.shuffle(midVal)
x, y = zip(*midVal)
x = list(x)
y = list(y) numBatches = len(x) // batchSize for i in range(numBatches):
start = i * batchSize
end = start + batchSize
batchX =x[start: end]
batchY = y[start: end] yield batchX, batchY

7 模型结构

  在这里我们输入的不再是词的索引表示的数据,而是动态生成了词向量的数据,因此inputX的维度是三维。另外在输入到Bi-LSTM之前,加一个全连接层,可以训练输入的词向量,否则就是将ELMo的词向量直接输入到Bi-LSTM中,而这样的词向量可能并不是最优的词向量。

# 构建模型
class BiLSTMAttention(object):
""""""
def __init__(self, config): # 定义模型的输入
self.inputX = tf.placeholder(tf.float32, [None, config.sequenceLength, config.model.embeddingSize], name="inputX")
self.inputY = tf.placeholder(tf.float32, [None, 1], name="inputY") self.dropoutKeepProb = tf.placeholder(tf.float32, name="dropoutKeepProb") # 定义l2损失
l2Loss = tf.constant(0.0) with tf.name_scope("embedding"):
embeddingW = tf.get_variable(
"embeddingW",
shape=[config.model.embeddingSize, config.model.embeddingSize],
initializer=tf.contrib.layers.xavier_initializer()) reshapeInputX = tf.reshape(self.inputX, shape=[-1, config.model.embeddingSize]) self.embeddedWords = tf.reshape(tf.matmul(reshapeInputX, embeddingW), shape=[-1, config.sequenceLength, config.model.embeddingSize])
self.embeddedWords = tf.nn.dropout(self.embeddedWords, self.dropoutKeepProb) # 定义两层双向LSTM的模型结构
with tf.name_scope("Bi-LSTM"):
for idx, hiddenSize in enumerate(config.model.hiddenSizes):
with tf.name_scope("Bi-LSTM" + str(idx)):
# 定义前向LSTM结构
lstmFwCell = tf.nn.rnn_cell.DropoutWrapper(tf.nn.rnn_cell.LSTMCell(num_units=hiddenSize, state_is_tuple=True),
output_keep_prob=self.dropoutKeepProb)
# 定义反向LSTM结构
lstmBwCell = tf.nn.rnn_cell.DropoutWrapper(tf.nn.rnn_cell.LSTMCell(num_units=hiddenSize, state_is_tuple=True),
output_keep_prob=self.dropoutKeepProb) # 采用动态rnn,可以动态的输入序列的长度,若没有输入,则取序列的全长
# outputs是一个元祖(output_fw, output_bw),其中两个元素的维度都是[batch_size, max_time, hidden_size],fw和bw的hidden_size一样
# self.current_state 是最终的状态,二元组(state_fw, state_bw),state_fw=[batch_size, s],s是一个元祖(h, c)
outputs_, self.current_state = tf.nn.bidirectional_dynamic_rnn(lstmFwCell, lstmBwCell,
self.embeddedWords, dtype=tf.float32,
scope="bi-lstm" + str(idx)) # 对outputs中的fw和bw的结果拼接 [batch_size, time_step, hidden_size * 2], 传入到下一层Bi-LSTM中
self.embeddedWords = tf.concat(outputs_, 2)
# 将最后一层Bi-LSTM输出的结果分割成前向和后向的输出
outputs = tf.split(self.embeddedWords, 2, -1) # 在Bi-LSTM+Attention的论文中,将前向和后向的输出相加
with tf.name_scope("Attention"):
H = outputs[0] + outputs[1] # 得到Attention的输出
output = self._attention(H)
outputSize = config.model.hiddenSizes[-1] # 全连接层的输出
with tf.name_scope("output"):
outputW = tf.get_variable(
"outputW",
shape=[outputSize, 1],
initializer=tf.contrib.layers.xavier_initializer()) outputB= tf.Variable(tf.constant(0.1, shape=[1]), name="outputB")
l2Loss += tf.nn.l2_loss(outputW)
l2Loss += tf.nn.l2_loss(outputB)
self.predictions = tf.nn.xw_plus_b(output, outputW, outputB, name="predictions")
self.binaryPreds = tf.cast(tf.greater_equal(self.predictions, 0.0), tf.float32, name="binaryPreds") # 计算二元交叉熵损失
with tf.name_scope("loss"): losses = tf.nn.sigmoid_cross_entropy_with_logits(logits=self.predictions, labels=self.inputY)
self.loss = tf.reduce_mean(losses) + config.model.l2RegLambda * l2Loss def _attention(self, H):
"""
利用Attention机制得到句子的向量表示
"""
# 获得最后一层LSTM的神经元数量
hiddenSize = config.model.hiddenSizes[-1] # 初始化一个权重向量,是可训练的参数
W = tf.Variable(tf.random_normal([hiddenSize], stddev=0.1)) # 对Bi-LSTM的输出用激活函数做非线性转换
M = tf.tanh(H) # 对W和M做矩阵运算,W=[batch_size, time_step, hidden_size],计算前做维度转换成[batch_size * time_step, hidden_size]
# newM = [batch_size, time_step, 1],每一个时间步的输出由向量转换成一个数字
newM = tf.matmul(tf.reshape(M, [-1, hiddenSize]), tf.reshape(W, [-1, 1])) # 对newM做维度转换成[batch_size, time_step]
restoreM = tf.reshape(newM, [-1, config.sequenceLength]) # 用softmax做归一化处理[batch_size, time_step]
self.alpha = tf.nn.softmax(restoreM) # 利用求得的alpha的值对H进行加权求和,用矩阵运算直接操作
r = tf.matmul(tf.transpose(H, [0, 2, 1]), tf.reshape(self.alpha, [-1, config.sequenceLength, 1])) # 将三维压缩成二维sequeezeR=[batch_size, hidden_size]
sequeezeR = tf.squeeze(r) sentenceRepren = tf.tanh(sequeezeR) # 对Attention的输出可以做dropout处理
output = tf.nn.dropout(sentenceRepren, self.dropoutKeepProb) return output

8 性能指标函数

# 定义性能指标函数

def mean(item):
return sum(item) / len(item) def genMetrics(trueY, predY, binaryPredY):
"""
生成acc和auc值
"""
auc = roc_auc_score(trueY, predY)
accuracy = accuracy_score(trueY, binaryPredY)
precision = precision_score(trueY, binaryPredY)
recall = recall_score(trueY, binaryPredY) return round(accuracy, 4), round(auc, 4), round(precision, 4), round(recall, 4)

9 训练模型

  在训练模型时,我们需要动态的生成词向量表示,在session的全局下需要实例化BiLM模型,定义一个elmo的方法来动态的生成ELMO词向量。

# 训练模型

# 生成训练集和验证集
trainReviews = data.trainReviews
trainLabels = data.trainLabels
evalReviews = data.evalReviews
evalLabels = data.evalLabels # 定义计算图 with tf.Graph().as_default(): session_conf = tf.ConfigProto(allow_soft_placement=True, log_device_placement=False)
session_conf.gpu_options.allow_growth=True
session_conf.gpu_options.per_process_gpu_memory_fraction = 0.9 # 配置gpu占用率 sess = tf.Session(config=session_conf) # 定义会话
with sess.as_default():
cnn = BiLSTMAttention(config) # 实例化BiLM对象,这个必须放置在全局下,不能在elmo函数中定义,否则会出现重复生成tensorflow节点。
with tf.variable_scope("bilm", reuse=True):
bilm = BidirectionalLanguageModel(
config.optionFile,
config.weightFile,
use_character_inputs=False,
embedding_weight_file=config.tokenEmbeddingFile
)
inputData = tf.placeholder('int32', shape=(None, None)) # 调用bilm中的__call__方法生成op对象
inputEmbeddingsOp = bilm(inputData) # 计算ELMo向量表示
elmoInput = weight_layers('input', inputEmbeddingsOp, l2_coef=0.0) globalStep = tf.Variable(0, name="globalStep", trainable=False)
# 定义优化函数,传入学习速率参数
optimizer = tf.train.AdamOptimizer(config.training.learningRate)
# 计算梯度,得到梯度和变量
gradsAndVars = optimizer.compute_gradients(cnn.loss)
# 将梯度应用到变量下,生成训练器
trainOp = optimizer.apply_gradients(gradsAndVars, global_step=globalStep) # 用summary绘制tensorBoard
gradSummaries = []
for g, v in gradsAndVars:
if g is not None:
tf.summary.histogram("{}/grad/hist".format(v.name), g)
tf.summary.scalar("{}/grad/sparsity".format(v.name), tf.nn.zero_fraction(g)) outDir = os.path.abspath(os.path.join(os.path.curdir, "summarys"))
print("Writing to {}\n".format(outDir)) lossSummary = tf.summary.scalar("loss", cnn.loss)
summaryOp = tf.summary.merge_all() trainSummaryDir = os.path.join(outDir, "train")
trainSummaryWriter = tf.summary.FileWriter(trainSummaryDir, sess.graph) evalSummaryDir = os.path.join(outDir, "eval")
evalSummaryWriter = tf.summary.FileWriter(evalSummaryDir, sess.graph) # 初始化所有变量
saver = tf.train.Saver(tf.global_variables(), max_to_keep=5) # 保存模型的一种方式,保存为pb文件
# builder = tf.saved_model.builder.SavedModelBuilder("../model/textCNN/savedModel")
sess.run(tf.global_variables_initializer()) def elmo(reviews):
"""
对每一个输入的batch都动态的生成词向量表示
""" # tf.reset_default_graph()
# TokenBatcher是生成词表示的batch类
batcher = TokenBatcher(config.vocabFile)

# 生成batch数据
inputDataIndex = batcher.batch_sentences(reviews) # 计算ELMo的向量表示
elmoInputVec = sess.run(
[elmoInput['weighted_op']],
feed_dict={inputData: inputDataIndex}
) return elmoInputVec def trainStep(batchX, batchY):
"""
训练函数
""" feed_dict = {
cnn.inputX: elmo(batchX)[0], # inputX直接用动态生成的ELMo向量表示代入
cnn.inputY: np.array(batchY, dtype="float32"),
cnn.dropoutKeepProb: config.model.dropoutKeepProb
}
_, summary, step, loss, predictions, binaryPreds = sess.run(
[trainOp, summaryOp, globalStep, cnn.loss, cnn.predictions, cnn.binaryPreds],
feed_dict)
timeStr = datetime.datetime.now().isoformat()
acc, auc, precision, recall = genMetrics(batchY, predictions, binaryPreds)
print("{}, step: {}, loss: {}, acc: {}, auc: {}, precision: {}, recall: {}".format(timeStr, step, loss, acc, auc, precision, recall))
trainSummaryWriter.add_summary(summary, step) def devStep(batchX, batchY):
"""
验证函数
"""
feed_dict = {
cnn.inputX: elmo(batchX)[0],
cnn.inputY: np.array(batchY, dtype="float32"),
cnn.dropoutKeepProb: 1.0
}
summary, step, loss, predictions, binaryPreds = sess.run(
[summaryOp, globalStep, cnn.loss, cnn.predictions, cnn.binaryPreds],
feed_dict) acc, auc, precision, recall = genMetrics(batchY, predictions, binaryPreds) evalSummaryWriter.add_summary(summary, step) return loss, acc, auc, precision, recall for i in range(config.training.epoches):
# 训练模型
print("start training model")
for batchTrain in nextBatch(trainReviews, trainLabels, config.batchSize):
trainStep(batchTrain[0], batchTrain[1]) currentStep = tf.train.global_step(sess, globalStep)
if currentStep % config.training.evaluateEvery == 0:
print("\nEvaluation:") losses = []
accs = []
aucs = []
precisions = []
recalls = [] for batchEval in nextBatch(evalReviews, evalLabels, config.batchSize):
loss, acc, auc, precision, recall = devStep(batchEval[0], batchEval[1])
losses.append(loss)
accs.append(acc)
aucs.append(auc)
precisions.append(precision)
recalls.append(recall) time_str = datetime.datetime.now().isoformat()
print("{}, step: {}, loss: {}, acc: {}, auc: {}, precision: {}, recall: {}".format(time_str, currentStep, mean(losses),
mean(accs), mean(aucs), mean(precisions),
mean(recalls))) # if currentStep % config.training.checkpointEvery == 0:
# # 保存模型的另一种方法,保存checkpoint文件
# path = saver.save(sess, "../model/textCNN/model/my-model", global_step=currentStep)
# print("Saved model checkpoint to {}\n".format(path)) # inputs = {"inputX": tf.saved_model.utils.build_tensor_info(cnn.inputX),
# "keepProb": tf.saved_model.utils.build_tensor_info(cnn.dropoutKeepProb)} # outputs = {"binaryPreds": tf.saved_model.utils.build_tensor_info(cnn.binaryPreds)} # prediction_signature = tf.saved_model.signature_def_utils.build_signature_def(inputs=inputs, outputs=outputs,
# method_name=tf.saved_model.signature_constants.PREDICT_METHOD_NAME)
# legacy_init_op = tf.group(tf.tables_initializer(), name="legacy_init_op")
# builder.add_meta_graph_and_variables(sess, [tf.saved_model.tag_constants.SERVING],
# signature_def_map={"predict": prediction_signature}, legacy_init_op=legacy_init_op) # builder.save()

文本分类实战(九)—— ELMO 预训练模型的更多相关文章

  1. 文本分类实战(十)—— BERT 预训练模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  2. 文本分类实战(一)—— word2vec预训练词向量

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  3. 文本分类实战(八)—— Transformer模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  4. 文本分类实战(七)—— Adversarial LSTM模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  5. 文本分类实战(六)—— RCNN模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  6. 文本分类实战(五)—— Bi-LSTM + Attention模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  7. 文本分类实战(四)—— Bi-LSTM模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  8. 文本分类实战(三)—— charCNN模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

  9. 文本分类实战(二)—— textCNN 模型

    1 大纲概述 文本分类这个系列将会有十篇左右,包括基于word2vec预训练的文本分类,与及基于最新的预训练模型(ELMo,BERT等)的文本分类.总共有以下系列: word2vec预训练词向量 te ...

随机推荐

  1. 17-Flink消费Kafka写入Mysql

    戳更多文章: 1-Flink入门 2-本地环境搭建&构建第一个Flink应用 3-DataSet API 4-DataSteam API 5-集群部署 6-分布式缓存 7-重启策略 8-Fli ...

  2. linux centos6.5安装KVM

    1.安装kvm,vnc软件 http://www.server110.com/kvm/201404/10467.html yum install kvm,缺啥补啥 yum install kvm km ...

  3. [二]Java虚拟机 jvm内存结构 运行时数据内存 class文件与jvm内存结构的映射 jvm数据类型 虚拟机栈 方法区 堆 含义

    前言简介 class文件是源代码经过编译后的一种平台中立的格式 里面包含了虚拟机运行所需要的所有信息,相当于 JVM的机器语言 JVM全称是Java Virtual Machine  ,既然是虚拟机, ...

  4. Zabbix监控原理及架构

    什么是Zabbix? Zabbix是一个用于网络,操作系统和应用程序的开源监控软件,它旨在监视和跟踪各种网络服务,服务器和其他网络硬件的状态. 为什么需要对各类系统进行监控? 在系统构建时的正常流程中 ...

  5. http服务器搭建之apache与nginx的特性

    一.了解一些简单的概念 HTTP服务器程序: httpd(apache) nginx lighttpd(功能和nginx差不多,活跃度不如ng) 应用程序服务器: IIS Tomcat(开源jsp容器 ...

  6. 【.NET Core项目实战-统一认证平台】第八章 授权篇-IdentityServer4源码分析

    [.NET Core项目实战-统一认证平台]开篇及目录索引 上篇文章我介绍了如何在网关上实现客户端自定义限流功能,基本完成了关于网关的一些自定义扩展需求,后面几篇将介绍基于IdentityServer ...

  7. IOS中armv7,armv7s,arm64以及i386和x86_64讲解

    一.前言问题 在iOS 开发过程中,估计比较少的人会在意armv7,armv7s,arm64这些概念,如果在意可能也是项目中出现了像下面的问题,才会想起来解决这些问题,但还是不是特别的理解,这些概念, ...

  8. 利用SQL活动和监视器找出耗时与占用CPU较高的不良SQL语句

    SQL活动和监视器是SQL Server自带的一个功能,在不断的升级优化中功能也越来越强大. 运行方式:服务器-->右键-->活动和监视器 可以看到很多数据库重要的运行状态. 1.等待任务 ...

  9. https处理的一个过程,对称加密和非对称加密

    一,对称加密 所谓对称加密,就是它们在编码时使用的密钥e和解码时一样d(e=d),我们就将其统称为密钥k. 对称加解密的过程如下: 发送端和接收端首先要共享相同的密钥k(即通信前双方都需要知道对应的密 ...

  10. Android 修改包名,导致安装错误

    一.app运行安装时出错 [安装时出错]: [百度翻译] 安装失败的消息未能敲定会议:install_failed_invalid_apk:/数据/应用程序/ vmdl292154713.tmp/9_ ...