GCN与强化学习
1. GCN
什么是GCN
G是Graph,C是卷积,卷积神经网络 一般称作 CNN;
CNN的原理是想构造一个小的窗口,然后在窗口里提取一些特征,窗口在图上不断滚动,最后提取一些特点;
GCN跟CNN也是类似,它是在Graph上的操作,CNN是在Image上;
GCN的背景(Graph Convolutional Networks):
- AI领域三分天下:计算机视觉CV,自然语言处理NLP,推荐系统 RS
- 图像识别的对象是图像,二维的结构 => 使用CNN模型提取图片特征
- CNN处理的图像或者视频中像素点(pixel)是排列成成很整齐的矩阵
- CNN的核心在于它的kernel,也就是一个个小窗口,在图片上平移,通过卷积的方式来提取特征
- 关键在于图片结构上的平移不变性,即一个小窗口无论移动到图片的哪一个位置,其内部的结构都是一模一样的,因此CNN可以实现参数共享
CNN一般作用于欧式空间,无法作用于非欧式空间,具有以下几个特点:
- 权重共享:同一个卷积核可以作用于不同的位置
- 局部性:欧式空间可以简洁的支持卷积核(直接根据卷积公式计算卷积结果即可),卷积核的大小一般远小于输入信号的大小
- 多尺度:CNN往往包含下采样,可以减少参数,并获得更大的感受野(receptive field)
GCN的背景:
- 自然语言处理操作对象是序列信息,一维的结构 => 使用RNN模型针对序列结构进行操作,使得序列前后的信息互相影响,很好地捕捉序列的特征
- 图片或者自然语言,都属于欧式空间的数据 => 有维度的概念,数据的特点是结构规则
- 现实生活中,很多是不规则的数据结构,典型的就是图结构,或称拓扑结构,如社交网络、化学分子结构、知识图谱等等
- 即使是语言,实际上其内部也是复杂的树形结构,也是一种图结构
- 针对图片,在做目标识别的时候,我们关注的实际上只是二维图片上的部分关键点,这些点组成的也是一个图的结构
GCN与NLP:
- Graph Convolutional Networks for Text Classification, AAAI 2019
- 用图卷积神经网络GCN做文本分类
- https://arxiv.org/abs/1809.05679
- 文本分类是NLP中的一个很基础的问题(经典的自然语言处理叫文本分类,句子属于哪个类型,就可以用GCN,把它看做图)
什么是GCN:
- Graph Convolutional Networks,图卷积神经网络,实际上跟CNN的作用一样,就是一个特征提取器,只不过它的对象是图数据
- GCN精妙地设计了一种从图数据中提取特征的方法,从而让我们可以使用这些特征去对图数据进行处理:
- 节点分类(node classification)
- 图分类(graph classification)
- 边预测(link prediction)
- 图嵌入表示(graph embedding)
- GCN应用已经渗透在CV,NLP,RS中
GCN原理
GCN算法:
- Semi-Supervised Classification with Graph Convolutional Networks, 2017
- https://arxiv.org/abs/1609.02907
- 具有可扩展的优点,能够适应网络的变化
- 相较于起传统的深度学习,图神经网络由于在关系数据挖掘方面有巨大的潜力,正成为学术界和工业界投入研发的热点,比如商品推荐,金融风控,聊天机器人中的语义分析及意图识别
GCN算法
GCN 用作 特征提取 ---> 分类
对于一个图网络G=(V,E),顶点node和边edge,有N个节点,每个节点都有自己的特征,目标是要学习图上的信号或特征的一个映射;
邻接矩阵 A
度矩阵 D
度 - 边的个数
拉普拉斯矩阵
- L=D-A,其中L为拉普拉斯矩阵,D为Graph中节点的度,A为图的邻接矩阵; (度矩阵D - 邻接矩阵A)
常用的拉普拉斯矩阵有3种
- Combinatorial Laplacian: L = D - A
- Symmetric normalized Laplacian: GCN采用 Lsys = D-1/2 AD-1/2
- Random walk normalized Laplacian:Lrw = D-1 A
使用拉普拉斯矩阵的好处
- 拉普拉斯是对称矩阵,可以进行特征分解(谱分解)
- 只在中心顶点和一阶相连的顶点上(1-hop neighbor)有非0元素,其余之处均为0
- 通过拉普拉斯算子与拉普拉斯矩阵进行类比
GCN算法:
- 对于一个图网络G=(V,E),有N个节点,每个节点都有自己的特征,目标是要学习图上的信号或特征的一个映射
- GCN模型的输入为矩阵X和A:
- 矩阵X,表示这些节点特征,N×D维矩阵
- 矩阵A,表示各个节点之间的关系,N×N维矩阵,也称为邻接矩阵(adjacency matrix)
- GCN是一个神经网络层,层与层之间的传播方式
GCN算法:
- 公式解释:http://tkipf.github.io/graph-convolutional-networks/
- 每一层GCN的输入都是邻接矩阵A和node的特征H,如果我们直接做一个内积,乘一个参数矩阵W,再激活一下,就相当于一个简单的神经网络层 f(H(l), A) = σ(AH(l) W(l))
实验证明,使用这个简单的神经网络层,就已经很强大了不过这个简单模型有2个局限性:
- 局限1,只使用A的话,由于A的对角线上都是0,所以在和特征矩阵H相乘的时候,只会计算一个node的所有邻居的特征的加权和,该node本身的特征却被忽略了
- 可以做一个小改动,即给A加上一个单位矩阵I,这样就让对角线元素变成1了
- 局限2,A是没有经过归一化的矩阵,如果A与特征矩阵H相乘会改变特征原本的分布,产生一些不可预测的问题
- 对A做一个标准化处理,首先让A的每一行加起来为1,然后对A乘以D-1 ,D为度矩阵,可以解决局限2
- 进一步把D-1 拆开与A相乘,得到一个对称且归一化的矩阵:D-1/2AD-1/2
结合这两种改进的方式,得到:
Project A:美国大学生足球队Embedding(GCN)
import networkx as nx # 对网络G进行可视化 def plot_graph(G): plt.figure() pos = nx.spring_layout(G) edges = G.edges() nx.draw_networkx(G, pos, edges=edges); nx.draw_networkx_nodes(G, pos, nodelist=G.nodes(), node_size=300, node_color='r', alpha=0.8) nx.draw_networkx_edges(G, pos, edgelist=edges,alpha =0.4) plt.show() # 数据加载,构造图 G = nx.read_gml('football.gml') # 可视化 plot_graph(G) print(list(G.nodes())) print(G.nodes['BrighamYoung']['value'])
# 构建GCN,计算A_hat和D_hat矩阵 #按照字母顺序排序 order = sorted(list(G.nodes())) print(order) A = to_numpy_matrix(G, nodelist=order) # 邻接矩阵 print(A) # 生成对角矩阵 I = np.eye(G.number_of_nodes()) A_hat = A + I print(A_hat)
# D_hat为A_hat的度矩阵 D_hat = np.array(np.sum(A_hat, axis=0))[0] print('D_hat=\n', D_hat) # 得到对角线上的元素 D_hat = np.matrix(np.diag(D_hat)) print('D_hat=\n', D_hat)
# 初始化权重 W_1 = np.random.normal(loc=0, scale=1, size=(G.number_of_nodes(), 4)) W_2 = np.random.normal(loc=0, size=(W_1.shape[1], 2)) print('W_1=\n', W_1) print('W_2=\n', W_2)
# x<0时 结果=0; x>=0时,结果=x def relu(x): return(abs(x)+x)/2 # 叠加GCN层,这里只使用单位矩阵作为特征表征,即每个节点被表示为一个 one-hot 编码的类别变量 def gcn_layer(A_hat, D_hat, X, W): return relu(D_hat**-1 * A_hat * X * W) H_1 = gcn_layer(A_hat, D_hat, I, W_1) H_2 = gcn_layer(A_hat, D_hat, H_1, W_2) output = H_2 print('output=\n', output) # 提取特征表征 feature_representations = {} nodes = list(G.nodes()) for i in range(len(nodes)): feature_representations[nodes[i]] = np.array(output)[i] print('feature_representations=\n', feature_representations)
# 不同节点value,绘制不同的颜色 def getValue(value): colorList = ['blue','green','purple','yellow','red','pink','orange','black','white','gray','brown','wheat'] return colorList[int(value)] # 绘制output,节点GCN embedding可视化 def plot_node(output): for i in range(len(nodes)): node_name = nodes[i] value = G.nodes[node_name]['value'] plt.scatter(np.array(output)[i,0],np.array(output)[i,1] ,label=str(i),color = getValue(value),alpha=0.5,s = 250) plt.text(np.array(output)[i,0],np.array(output)[i,1] ,i, horizontalalignment='center',verticalalignment='center', fontdict={'color':'black'}) plt.show() plot_node(output)
# 尝试去掉激活函数relu,重新运行一遍,发现效果反而更好 def gcn_layer(A_hat, D_hat, X, W): return D_hat**-1 * A_hat * X * W H_1 = gcn_layer(A_hat, D_hat, I, W_1) H_2 = gcn_layer(A_hat, D_hat, H_1, W_2) output = H_2 plot_node(output)
Project B:恶意软件检测
https://www.kaggle.com/ang3loliveira/malware-analysis-datasets-api-call-sequences
包含42,797个恶意软件(malware)API调用序列和1,079个正常软件(goodware)API调用序列
每个API调用序列由前100个非重复序列组成
恶意软件检测流程:
Step1,PE Files,Portable Executable,可执行文件
Step2,沙盒,轮流执行PE文件
Step3,产生raw JSON reports
Step4,从生成的JSON reports中抽取API调用序列
Step5,基于API调用序列,生成行为图(Behavioral graphs)
Step6,使用图卷积层对行为图进行高维特征抽取,如果有多个GCN层叠加到一起形成一个深度的网络,需要把这些GCN的输出结果拼接起来,这样可以得到不同尺度的特征
Step7,将学到的特征输入给FC层
Step8,使用sigmoid层进行二分类
算法流程:
Step1,对于一个API调用序列x,计算其邻接矩阵表
Step2,使用1个或多个GCN层对API调用序列x进行特征抽取 Graph Embedding
其中A= Seq2BGraph(x, |N|) 得到邻近矩阵
算法流程:
- 对于API调用序列x=(0, 1, 2, 0, 2, 3)
- 排序好的API集合为 N=(0, 1, 2, 3)
- 计算邻近矩阵A
- 计算AX
- AX表明了API调用序列,以及每个API调用的入度邻居(indegree neighbors)
- A表明了顺序的节点,X表明了这些节点的行为
算法流程:
每一层GCN的输入都是邻接矩阵A和node的特征H,如果我们直接做一个内积,乘一个参数矩阵W,再激活一下,就相当于一个简单的神经网络层
简单模型存在2个局限性,进行改进:
对于2个叠加的GCN层,我们将他们拼接起来
Project B:恶意软件检测(GCN)
# 数据加载 df = pd.read_csv('dynamic_api_call_sequence_per_malware_100_0_306.csv') print(df.head()) print(df.info()) # 得到特征X和目标y X = df.drop(['hash', 'malware'], axis = 1).values.astype(int) y = df['malware'].values.astype(int) print(X.shape) print(y.shape)
GCN工具
# 数据探索,说明X特征矩阵取值为0-306 print(X.min()) print(X.max()) # 检查样本是否均匀 def check_imbalance(dataset): count = sorted(Counter(dataset).items()) print(count) print(count[1][1] / count[0][1]) return check_imbalance(y) # 训练集70%,测试集30% X_train, X_test, y_train, y_test = train_test_split(X, y, test_size = 0.3, random_state = SEED) check_imbalance(y_train) check_imbalance(y_test) # 释放不用的变量 del df, X, y # 计算邻近矩阵A和D矩阵 def norn_adj(X, input_dim_1): #计算邻接矩阵 temp = X.cpu().numpy() A_adj = np.zeros([X.size(0), input_dim_1, input_dim_1]) for i in range(temp.shape[0]): for j in range(temp.shape[1]): x1 = int(temp[i,j]) if j!=(temp.shape[1]-1): x2 = int(temp[i,j+1]) A_adj[i][x1][x2] = 1.0 A = torch.from_numpy(A_adj).float().cuda() A_hat = A + torch.eye(input_dim_1, dtype = torch.float).cuda() D_hat = A_hat.sum(dim = 1).pow(-1.0).diag_embed() return A_hat, D_hat # 对X进行one_hot特征提取 def to_one_hot(X, input_dim_1): X = F.one_hot(X, num_classes = input_dim_1).float() X = X.permute(0, 2, 1) return X class GCN_network(nn.Module): def __init__(self, weight_dim_1, weight_dim_2): super(GCN_network, self).__init__() self.weight_dim_1 = weight_dim_1 self.weight_dim_2 = weight_dim_2 self.weights = nn.Parameter(torch.rand((self.weight_dim_1, weight_dim_2), dtype = torch.float, requires_grad = True)) # 前向传播 def forward(self, A_hat, D_hat, X): return D_hat.matmul(A_hat).matmul(X).matmul(self.weights) # 使用1层GCN层 H_list_model_1 = [] class Model_1_network(nn.Module): def __init__(self, input_dim_1, input_dim_2, weight_dim_2, dropout_rate): super(Model_1_network, self).__init__() self.input_dim_1 = input_dim_1 self.input_dim_2 = input_dim_2 self.weight_dim_1 = input_dim_2 self.weight_dim_2 = weight_dim_2 self.dropout_rate = dropout_rate # 定义GCN层 self.gcn = GCN_network(self.weight_dim_1, self.weight_dim_2) # 定义dropout self.dropout = nn.Dropout(p = self.dropout_rate) # 定义FC层 self.fc = nn.Linear(self.input_dim_1 * self.weight_dim_2, 1) torch.manual_seed(SEED) input_dim_1 = 307 input_dim_2 = 100 weight_dim_2 = 31 dropout_rate = 0.6 batch_size = 128 max_epochs = 2 # 使用GCN做二分类 model_1 = NeuralNetBinaryClassifier( Model_1_network, module__input_dim_1 = input_dim_1, module__input_dim_2 = input_dim_2, module__weight_dim_2 = weight_dim_2, module__dropout_rate = dropout_rate, batch_size = batch_size, max_epochs = max_epochs, train_split = None, optimizer = torch.optim.Adam, iterator_train__shuffle = True, device = 'cuda' ) pipe = Pipeline([ ('model', model_1) ]) # 模型训练 pipe.fit(X_train, y_train.astype(np.float)) from sklearn.metrics import accuracy_score, recall_score, confusion_matrix from sklearn.metrics import precision_score, f1_score, roc_auc_score, roc_curve # 模型评估 def model_evaluate(y, pred): print('Confusion matrix\n[TN FP]\n[FN TP]') # confusion_matrix(y_true, y_pred) print(confusion_matrix(y >= 0.5, pred >= 0.5)) print(f'F1-Score: {f1_score(y >= 0.5, pred >= 0.5):.4f}') print(f'ROC AUC: {roc_auc_score(y, pred):.4f}') return def visualize(X, y, points, n_features): points = np.arange(points) np.random.shuffle(points) color = ['red' if l == 1 else 'green' for l in y[points]] for i in range(n_features): for j in range(n_features): if j > i: plt.scatter(X[points, i], X[points, j], color = color) plt.pause(0.1) return # 使用PCA压缩到2维,进行可视化 def visualize_pca(X, y): # 归一化 X = X / np.max(X) # 使用PCA进行压缩 pca = decomposition.PCA(n_components = 2) # 可视化 visualize(pca.fit_transform(X), y, X.shape[0], 2) return H_list_model_1.clear() # 得到预测结果,并进行评估 X_test_predictions = pipe.predict_proba(X_test)[:,1] model_evaluate(y_test, np.ones(len(y_test))) model_evaluate(y_test, X_test_predictions) # 绘制ROC曲线 fpr, tpr, thresholds = roc_curve(y_test, X_test_predictions) pyplot.plot([0, 1], [0, 1], linestyle='--') pyplot.plot(fpr, tpr) pyplot.show() result = torch.zeros(1, 9517) for i in H_list_model_1: result = torch.cat([result, i.cpu()], 0) X_result = result.numpy()[1:] visualize_pca(X_result, y_test)
Project B:恶意软件检测(LSTM)
# 使用LSTM进行预测 H_list_lstm = [] # 定义LSTM class LSTM_network(nn.Module): def __init__(self, input_dim, hidden_dim, dropout_rate): super(LSTM_network, self).__init__() self.input_dim = input_dim self.hidden_dim = hidden_dim self.dropout_rate = dropout_rate self.lstm = nn.LSTM(self.input_dim, self.hidden_dim, batch_first = True) self.dropout = nn.Dropout(p = self.dropout_rate) self.fc = nn.Linear(self.hidden_dim, 1) def forward(self, X): # 对X进行one_hot特征提取 X = F.one_hot(X, num_classes = self.input_dim).float().cuda() # 隐藏层形状:(num_layers, batch_size, hidden_dim) hidden_0 = (torch.zeros(1, X.size(0), self.hidden_dim).float().cuda(), torch.zeros(1, X.size(0), self.hidden_dim).float().cuda()) # 输入/输出形状:(batch_size, seq_len, input_dim) _, self.hidden = self.lstm(X, hidden_0) H = self.hidden[0].squeeze() H = self.dropout(H) # 可视化时用到 H_list_lstm.append(H.cpu()) H = self.fc(H) return H.squeeze() torch.manual_seed(SEED) # LSTM超参数设置 input_dim = 307 hidden_dim = 100 dropout_rate = 0.5 batch_size = 128 max_epochs = 2 # 使用LSTM做二分类 LSTM = NeuralNetBinaryClassifier( LSTM_network, module__input_dim = input_dim, module__hidden_dim = hidden_dim, module__dropout_rate = dropout_rate, batch_size = batch_size, max_epochs = max_epochs, train_split = None, optimizer = torch.optim.Adam, iterator_train__shuffle = True, device = 'cuda' ) # 定义Pipeline pipe = Pipeline([ ('model', LSTM) ]) # 使用LSTM进行训练 pipe.fit(X_train, y_train.astype(np.float)) # 使用LSTM进行预测 H_list_lstm = [] # 定义LSTM class LSTM_network(nn.Module): def __init__(self, input_dim, hidden_dim, dropout_rate): super(LSTM_network, self).__init__() self.input_dim = input_dim self.hidden_dim = hidden_dim self.dropout_rate = dropout_rate self.lstm = nn.LSTM(self.input_dim, self.hidden_dim, batch_first = True) self.dropout = nn.Dropout(p = self.dropout_rate) self.fc = nn.Linear(self.hidden_dim, 1) def forward(self, X): # 对X进行one_hot特征提取 X = F.one_hot(X, num_classes = self.input_dim).float().cuda() # 隐藏层形状:(num_layers, batch_size, hidden_dim) hidden_0 = (torch.zeros(1, X.size(0), self.hidden_dim).float().cuda(), torch.zeros(1, X.size(0), self.hidden_dim).float().cuda()) # 输入/输出形状:(batch_size, seq_len, input_dim) _, self.hidden = self.lstm(X, hidden_0) H = self.hidden[0].squeeze() H = self.dropout(H) # 可视化时用到H_list_lstm H_list_lstm.append(H.cpu()) H = self.fc(H) return H.squeeze() H_list_lstm.clear() # 得到预测结果 X_test_predictions = pipe.predict_proba(X_test)[:,1] model_evaluate(y_test, np.ones(len(y_test))) print(X_test_predictions_1) print(y_test) # 对LSTM预测结果进行评估 model_evaluate(y_test, X_test_predictions) # 绘制ROC曲线 fpr, tpr, thresholds = roc_curve(y_test, X_test_predictions) pyplot.plot([0, 1], [0, 1], linestyle='--') pyplot.plot(fpr, tpr) pyplot.show() visualize_pca(X_test, y_test) result = torch.zeros(1, 100) for i in H_list_lstm: result = torch.cat([result, i.cpu()], 0) X_result = result.numpy()[1:] visualize_pca(X_result, y_test)
Summary
- 对于图像、语音、文本信息,这种一维、二维的矩阵表示的数据,有天然的维度对齐特性,即使不同的数据也可以对齐到同样的特征尺度和矩阵维度 => CNN 或者 RNN
- 存在大量的信息,比如社交网络,知识图谱等,是非欧几里得数据,即每个节点可能有不一样的连接方式,在深度学习中常见的处理方法是转换成列矩阵或按最大图结构转换成稀疏矩阵,这两种方法会
- 导致空间信息的丢失或者特征维度的膨胀,采用图卷积是当前对这种数据最有效的分析处理方式
- 采用GCN模型对 API call sequence进行二分类,是将每个样本是一个graph,此外我们也可以采用node级别的Graph Embedding
- 针对node级别Embedding,需要人工定义边规则,比如简单的多少重复api算有边(两个节点之间10次调用,就算有边)
在Netflix Prize中,对电影进行打分预测是经典的推荐系统评分预测问题
实际上用户之间是有关联的,比如你的朋友看了一部电影,你就很有可能去看,增加这个维度的信息就有了 基于Graph的矩阵补全(Geometric matrix completion)
此外电影之间也有关联,比如同一个导演拍摄的电影,同一个主题系列等,增加这个维度的关联信息,就有了 Multi-graph convolution
- GCN的理论基础是spectral domain,借助图的理论来实现拓扑图上的卷积操作
- GCN有很广泛的应用场景,比如大规模交通路网速度预测,社交网络分析,生物网络,金融风控,商品推荐,聊天机器人中的语义分析及意图识别
- Node2Vec在工业界很成功:
- Facebook:广告领域定制化受众
- Tencent:微信朋友圈广告(Lookalike)策略
- Lookalike,相似人群扩展
- DMP是Look-alike技术的核心基础
2. 强化学习
Project A:迷宫问题
- 迷宫为5个房间(实际可以为N个房间),房间与房间之间通过门连接,编号0到4,5号是房子外边,即我们想要达到的终点
- 将AI随机放在任一房间内,如何找到终点的路径
Project B:FlappyBird游戏
- 小鸟穿过的水管越多 => 分数越高
- 只有一条生命,如果撞到了水管上 => Game Over
- 训练Agent,得到更多的reward分数
强化学习(Reinforcement Learning):
- 机器学习的一个分支:监督学习、无监督学习、强化学习
- 强化学习的思路和人比较类似,是在实践中学习
- 比如学习走路,如果摔倒了,那么我们大脑后面会给一个负面的奖励值 => 这个走路姿势不好;如果后面正常走了一步,那么大脑会给一个正面的奖励值 => 这是一个好的走路姿势
- 与监督学习的区别,没有监督学习已经准备好的训练数据输出值,强化学习只有奖励值,但是这个奖励值和监督学习的输出值不一样,它不是事先给出的,而是延后给出的(比如走路摔倒)
- 与非监督学习的区别,在非监督学习中既没有输出值也没有奖励值的,只有数据特征,而强化学习有奖励值(为负是为惩罚),此外非监督学习与监督学习一样,数据之间也都是独立的,没有强化学习
- 这样的前后依赖关系
强化学习(Reinforcement Learning):
- 可以应用于不同领域:神经科学、心理学、计算机科学、工程领域、数学、经济学等
强化学习的特点:
- 没有监督数据、只有奖励信号
- 奖励信号不一定是实时的,很可能是延后的,甚至延后很多
- 时间(序列)是一个重要因素
- 当前的行为影响后续接收到的数据
强化学习有广泛的应用:游戏AI,推荐系统,机器人仿真,投资管理,发电站控制
强化学习与机器学习:
- 强化学习没有教师信号,也没有label,即没有直接指令告诉机器该执行什么动作
- 反馈有延时,不能立即返回
- 输入数据是序列数据,是一个连续的决策过程,比如AlphaGo下围棋的Agent,可以不使用监督学习:
- 请一位围棋大师带我们遍历许多棋局,告诉我们每个位置的最佳棋步,这个代价很贵expensive
- 很多情况下,没有最佳棋步,因为一个棋步的好坏依赖于其后的多个棋步
- 使用强化学习,整个过程唯一的反馈是在最后(赢or输)
基本概念:
- 个体,Agent,学习器的角色,也称为智能体
- 环境,Environment,Agent之外一切组成的、与之交互的事物
- 动作,Action,Agent的行为
- 状态,State,Agent从环境获取的信息
- 奖励,Reward,环境对于动作的反馈
- 策略,Policy,Agent根据状态进行下一步动作的函数
- 状态转移概率,Agent做出动作后进入下一状态的概率
四个重要的要素:状态(state)、动作(action)、策略(policy)、奖励(reward)
Environment(状态、奖励)可以看作妈妈,Agent(动作、策略)看作宝宝,
强化学习:
- RL考虑的是个体(Agent)与环境(Environment)的交互问题
- 目标是找到一个最优策略,使Agent获得尽可能多的来自环境的奖励
- 比如赛车游戏,游戏场景是环境,赛车是Agent,赛车的位置是状态,对赛车的操作是动作,怎样操作赛车是策略,比赛得分是奖励
- 很多情况下,Agent无法获取全部的环境信息,而是通过观察(Observation)来表示环境(environment),也就是得到的是自身周围的信息
奖励(Reward)
- 是信号的反馈,是一个标量,它反映个体在t时刻做得怎么样,个体的工作就是最大化累计奖励
- 强化学习假设是,所有问题解决都可以被描述成最大化累积奖励
序列决策(Sequential Decision Making)
- 目标:选择一定的行为系列以最大化未来的总体奖励
- 这些行为可能是一个长期的序列
- 奖励可能而且通常是延迟的
- 有时候宁愿牺牲短期奖励,从而获取更多的长期奖励
个体与环境的交互(Agent & Environment)
- 从个体的视角:
- 在 t时刻,Agent可以收到环境的反馈:
- 有一个对于环境的观察评估Qt
- 做出一个行为 At
- 从环境得到一个奖励信号Rt+1
- 环境可以:
- 接收个体的动作 At
- 更新环境信息,同时使得个体可以得到下一个观测 Qt+1
- 给个体一个奖励信号 Rt+1
Project A:迷宫问题
QLearning:
- Q函数,也称为动作值函数,有两个输入:「状态」和「动作」,它将返回在这个状态下执行该动作的未来奖励期望
- 将agent随机放在任一房间内,每打开一个房门返回一个reward
- 根据房间之间连通性的关系,可以得到Reward矩阵(R矩阵)
- 可以把 Q 函数视为一个在 Q-table 上滚动的读取器
-1表示走不了,0 -> 4 (0状态是可行解,好坏没有做评估;-1是负反馈告诉它这个地方走不了)
5为出口,从1-> 5,直接走出去了,它的到的奖励就是100;假如4-5也是联通的(外面也可以走),它得到的也是100分;
Q-learning是典型的基于价值(Value)函数的强化学习方法
- 其中Q是一个数值(即价值value),在初始化时可能被赋予一个任意数值
- 在迭代时刻t,我们有状态 ,此时代理做出动作 ,然后得到奖励 ,从而进入到一个更新的状态 ,
- 然后对Q值进行更新,更新公式为
Q(st, at) 更新值 即在st在当下位置的情况下,采用at 可以拿到的分数;预期最优值max Q; γ为贴现因子,打折扣(比如A、B、C轮公司的估值给你个价值评估);
使用QLearning:
初始化Q表,用于记录状态-动作对的值,每个episode中的每一步都会根据公式更新一次Q表:
为了简便,将学习率α 设为1,更新公式为:
当前状态s的最大将来奖励等于下一状态s' 的最大将来奖励乘以折扣因子;
# 模拟迷宫问题 GAMMA = 0.8 Q = np.zeros((6,6)) R=np.asarray([[-1,-1,-1,-1,0,-1], [-1,-1,-1,0,-1,100], [-1,-1,-1,0,-1,-1], [-1,0, 0, -1,0,-1], [0,-1,-1,0,-1,100], [-1,0,-1,-1,0,100]]) # 取每一行的最大值 def getMaxQ(state): # 通过选取最大动作值来进行最优策略学习 return max(Q[state, :])
def QLearning(state): curAction = None for action in range(6): if(R[state][action] == -1): Q[state, action] = 0 else: curAction = action Q[state, action] = R[state][action] + GAMMA * getMaxQ(curAction) # 模拟1000次 for count in range(1000): for i in range(6): QLearning(i) # 显示保留小数点后一位 np.set_printoptions(precision=1) print(Q/5)
Project B:FlappyBird游戏
- PLE( PyGame-Learning-Environment )
- PyGame学习环境,PLE的目标是使从业人员可以专注于模型和实验的设计,而不是环境设计
- 目前PyGame包含的游戏有:Catecher/Monster Kong/FlappyBird/Pixelcopter/Pong/PuckWorld/RaycastMaze/Snake/WaterWorld
- 其中FlappyBird:游戏限制FPS为30,游戏窗口大小为288*512
PLE参数使用
https://pygame-learning-environment.readthedocs.io/en/latest/modules/ple.html
Qlearning:
一个(s, a)a对应一个Q值,可以把Q值看作一个很大的表格
横列代表s,纵列代表a,数值为Q值
使用PLE 模拟环境 state= { 'player_y': 256, 'player_vel': 0, 'next_pipe_dist_to_player': 309.0, 'next_pipe_top_y': 29, 'next_pipe_bottom_y': 129, 'next_next_pipe_dist_to_player': 453.0, 'next_next_pipe_top_y': 107, 'next_next_pipe_bottom_y': 207}
状态参数太多,进行降维处理;
状态参数选择:
- 1)小鸟的速度,player_vel
- 2)距离顶端水管的垂直距离,player_y - next_pipe_top_y
- 3)距离下一个水管的水平距离, next_pipe_dist_to_player
next_pipe_dist_to_player的范围为 [0, 288]
player_y-next_pipe_top_y的范围 [-512, 512] ,有1024个取值
Thinking:数据范围太大(288*1024*32),很难让函数收敛 => 训练时间长,如何减少训练时间?
缩小状态参数,将范围进行分段聚类
再进行降维,分桶-即分段
Thinking:以下的state有多少种可能? 20*1024*288 => 6*6*6=216, 减少了2.73万倍
速度: 32 -> 6种可能性; 高度: 1024 -> 6种; distance: 6种;
# 设置小鸟速度的等级 if velocity < -15: velocity_category = 0 elif velocity < -10: velocity_category = 1 elif velocity < -5: velocity_category = 2 elif velocity < 0: velocity_category = 3 elif velocity < 5: velocity_category = 4 else: velocity_category = 5 # 设置小鸟高度等级 if dist_to_pipe_bottom < 8: # very close height_category = 0 elif dist_to_pipe_bottom < 20: # close height_category = 1 elif dist_to_pipe_bottom < 50: # not close height_category = 2 elif dist_to_pipe_bottom < 125: # mid height_category = 3 elif dist_to_pipe_bottom < 250: # far height_category = 4 else: height_category = 5 # 设置distance等级 if dist_to_pipe_horz < 8: # very close dist_category = 0 elif dist_to_pipe_horz < 20: # close dist_category = 1 elif dist_to_pipe_horz < 50: # not close dist_category = 2 elif dist_to_pipe_horz < 125: # mid dist_category = 3 elif dist_to_pipe_horz < 250: # far dist_category = 4 else: dist_category = 5
选择最佳动作
针对状态s,选择最大的Q值对应的action
基于Qlearning的强化学习
- 将force_fps设置为True 进行快速fps
- env = PLE(game, fps=30, display_screen=True, force_fps=True)
运行结果 Episodes: 0, Current score: -5.0, Max score: 0 Episodes: 1, Current score: -5.0, Max score: 0 Episodes: 2, Current score: -5.0, Max score: 0 Episodes: 3, Current score: -5.0, Max score: 0 Episodes: 4, Current score: -5.0, Max score: 0 Episodes: 5, Current score: -5.0, Max score: 0 …… Episodes: 99, Current score: 19.0, Max score: 113.0 Episodes: 100, Current score: 13.0, Max score: 113.0 Episodes: 101, Current score: 85.0, Max score: 113.0 ……
基于DQN的强化学习
- Thinking:QTable的局限性,使用表格来表示Q(s, a),但是现实问题中状态太多,使用表格根本存不下,怎么办?
价值函数近似Value Function Approximation
Paddle安装
在官方主页上找到安装方式 https://www.paddlepaddle.org.cn/
PARL requires paddle >= 1.8.5 and paddle < 2.0.0
python -m pip install paddlepaddle==1.8.5 -i https://mirror.baidu.com/pypi/simple
Paddle安装
- 在官方主页上找到安装方式 https://www.paddlepaddle.org.cn/
- PARL requires paddle >= 1.8.5 and paddle < 2.0.0
- python -m pip install paddlepaddle==1.8.5 -i https://mirror.baidu.com/pypi/simple
DQN:
- DQN 的一个挑战是,神经网络是会遗忘之前的经验,因为它会不断用新的经验来覆盖掉之前的
- 所以需要一个列表来存储之前的经验,以便用于对模型训练
- 这个存储经验的列表叫做 memory,
- memory = [(state, action, reward, next_state, done)...]
- 经验回放,是用 memory 来训练神经网络
- 首先从 memory 中取样,随机选取 batch_size 个数据:
- minibatch = random.sample(self.memory, batch_size)
- target = reward + gamma * np.amax(model.predict(next_state))
- 然后模型通过 fit() 方法学习输入输出数据对,
- model.fit(state, reward_value, epochs=1, verbose=0)
强化学习:
- 属于机器学习的分支(其他还有监督学习、无监督学习)
- 与其他学习方法不同之处在于:强化学习是智能体从环境到行为映射的学习,目标是让奖励最大化。
- 如果智能体的某个行为决策得到了正向奖励,那么智能体在后续使用这个行为的决策趋势就会加强
- 强化学习是最接近于自然界的学习方式
- 强化学习与深度学习结合,可以解决海量数据泛化的问题(比如DeepMind的AlphaGO)
构建智能体与环境的反馈机制
- 以往的学习方式,大多为基于监督学习的方式 => 缺少有效的探索能力,造成system倾向给consumer推送曾经发生行为的item(比如商品、店铺、问题)
- 强化学习可以有效建立consumer与system之间的交互过程,同时可以最大化过程积累收益,在业务场景有很好的应用
强化学习的应用场景:
- 生产 Manufacturing
- 日本公司 Fanuc,工厂的机器人在拿起一个物体时,会捕捉这个过程的视频,记住它每次操作的行动(成功or失败),不断积累经验,下一次可以更快更准的行动
- 库存管理 Inventory Management
- 在库存管理中,库存量大,库存需求波动较大,而库存补货速度缓慢,通过建立强化学习算法来减少库存周转时间,提高空间利用率
动态定价 Dynamic pricing
- 强化学习中的 Q-learning 可以用来处理动态定价问题。
- 运输行业 Delivery Industry
- 制造商在向各个客户运输时,想要在满足客户的所有需求的同时降低车队总成本,通过 multi-agents 和 Q-learning,可以降低时间,减少车辆数量
- 运输公司也可以根据交通、天气和安全状况的变化实时优化旅行路线
- 电商个性化推荐 ECommerce Personalization
- 可以用强化学习算法来学习和分析顾客行为,定制产品和服务以满足客户的个性化需求
广告服务 Ad Serving
- LinUCB算法(属于强化学习算法 bandit 的一种算法),会尝试投放更广范围的广告(尽管过去还没有被浏览很多)
- 双 11 推荐场景中,阿里巴巴使用了深度强化学习与自适应在线学习,通过持续机器学习和模型优化建立决策引擎,对海量用户行为以及百亿级商品特征进行实时分析,帮助用户迅速发现宝贝,提高人和
- 商品的配对效率
- 金融交易策略
- 应用强化学习来评价交易策略,可以帮助用户建立交易策略,并实现投资目标
Thinking1:机器学习中的监督学习、非监督学习、强化学习有何区别
Thinking2:GCN/Graph Embedding 都有哪些应用场景
Thinking3:在交通流量预测中,如何使用Graph Embedding,请说明简要的思路
Thinking4:在文本分类中,如何使用Graph Embedding,请说明简要的思路
Action1:Dolphin数据集是 D.Lusseau 等人使用长达 7 年的时间观察新西兰 Doubtful Sound海峡 62 只海豚群体的交流情况而得到的海豚社会关系网络。这个网络具有 62 个节点,159 条边。节点表示海豚,边表示海豚间的频繁接触
- 数据文件:dolphins.gml
- 对Dolphin 关系进行Graph Embedding,可以使用DeepWalk, Node2Vec或GCN
- 对Embedding进行可视化(使用PCA呈现在二维平面上)
Action2:FlappyBird强化学习
- 小鸟穿过的水管越多 => 分数越高
- 只有一条生命,如果撞到了水管上 => Game Over
- 训练Agent,得到更多的reward分数
- TO DO 编写Qlearning/DQN策略,挑战 Score > 200
- 将训练的agent model保存好
- 通过训练好的agent model可以达到 score > 200
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步