当前位置: 首页 > 编程日记 > 正文

关于GCN,我有三种写法

作者 | 阿泽

来源 | 阿泽的学习笔记(ID: aze_learning)

本篇文章主要基于 DGL 框架用三种不同的方式来实现图卷积神经网络。

DGL简介

DGL(Deep Graph Library)框架是由纽约大学和 AWS 工程师共同开发的开源框架,旨在为大家提供一个在图上进行深度学习的工具,帮助大家更高效的实现算法。

用现有的一些框架比如 TensorFlow、Pytorch、MXNet 等实现图神经网络模型都不太方便,同样现有框架实现图神经网络模型的速度不够快。

DGL 框架设计理念主要在于将图神经网络看作是消息传递的过程,每一个节点会发出它自己的消息,也会接收来自其它节点的消息。然后在得到所有信息之后做聚合,计算出节点新的表示。原有的深度学习框架都是进行张量运算,但是图很多时候并不能直接表示成一个完整的张量,需要手动补零,这其实很麻烦,不高效。

DGL 是基于现有框架,帮助用户更容易实现图神经网络模型。DGL 现在主要是以消息传递的接口作为核心,同时提供图采样以及批量处理图的接口。

关于 DGL 就不再进行过多介绍,感兴趣的同学可以去官网(http://dgl.ai/)了解。

Prepare

import torch
import time
import math
import dgl
import numpy as np
import torch.nn as nn
from dgl.data import citation_graph as citegrh
from dgl import DGLGraph
import dgl.function as fn
import networkx as nx
import torch.nn.functional as Ffrom dgl.nn import GraphConv
# from dgl.nn.pytorch import GraphConv
# from dgl.nn.pytorch.conv import GraphConv

这里有三种导入方法,建议用第一种,因为 DGL 的开发同学设计了一个机制,会自动 detect 用了什么 beckend,从而适配对应的 backend 的 api。

print(torch.__version__)
print(dgl.__version__)
print(nx.__version__)
1.4.0
0.4.3
2.3GCN3.1 First version
DGL 的第一种写法是利用 DGL 预定义的图卷积模块 GraphConv 来实现的。
GCN 的数学公式如下:其中, 为节点的邻居集合, 表示节点度的平方根的乘积,用于归一化数据, 为激活函数。
GraphConv 模型参数初始化参考 tkipf 大佬的原始实现,其中  使用 Glorot uniform 统一初始化,并将偏差初始化为零。
简单介绍下 Glorot 均匀分布(uniform)
Glorot 均匀分布,也叫 Xavier 均匀分布,该方法源于 2010 年的一篇论文《Understanding the difficulty of training deep feedforward neural networks》。其核心思想在于:为了使得网络中信息更好的流动,每一层输出的方差应该尽量相等。基于这个目标,权重 W 的方差需要满足 ,我们知道均匀分布的方差为:。所以我们可以初始化 W 为 Xavier 均匀分布: (具体证明见论文)
class GCN(nn.Module):def __init__(self,g,in_feats,n_hidden,n_classes,n_layers,activation,dropout):super(GCN, self).__init__()self.g = gself.layers = nn.ModuleList()# input layerself.layers.append(GraphConv(in_feats, n_hidden, activation=activation))# output layerfor i in range(n_layers - 1):self.layers.append(GraphConv(n_hidden, n_hidden, activation=activation))# output layerself.layers.append(GraphConv(n_hidden, n_classes))self.dropout = nn.Dropout(p=dropout)def forward(self, features):h = featuresfor i, layers in enumerate(self.layers):if i!=0:h = self.dropout(h)h = layers(self.g, h)return h3.2 Second version3.2.1 ndataDGL 的第二种写法:使用用户自定义的 Message 和 Reduce 函数
ndata 是 DGL 的一个特殊的语法,可以用于赋值(获得)某些节点的特征:
x = tourch.randn(10, 3)
g.ndata['x'] = x如果指定某些节点的特征,可以进行切片操作:
g.ndata['x'][0] = th.zeros(1, 3)
g.ndata['x'][[0, 1, 2]] = th.zeros(3, 3)
g.ndata['x'][th.tensor([0, 1, 2])] = th.randn((3, 3))当然也可以获得边的特征:
g.edata['w'] = th.randn(9, 2)# Access edge set with IDs in integer, list, or integer tensor
g.edata['w'][1] = th.randn(1, 2)
g.edata['w'][[0, 1, 2]] = th.zeros(3, 2)
g.edata['w'][th.tensor([0, 1, 2])] = th.zeros(3, 2)# You can get the edge ids by giving endpoints, which are useful for accessing the features.
g.edata['w'][g.edge_id(1, 0)] = th.ones(1, 2)                   # edge 1 -> 0
g.edata['w'][g.edge_ids([1, 2, 3], [0, 0, 0])] = th.ones(3, 2)  # edges [1, 2, 3] -> 0
# Use edge broadcasting whenever applicable.
g.edata['w'][g.edge_ids([1, 2, 3], 0)] = th.ones(3, 2)          # edges [1, 2, 3] -> 03.2.2 UDFs在 DGL 中,通过用户自定义的函数(User-defined functions,UDFs)来实现消息传递和节点特征变换。
可以利用 Edge UDFs 来定义一个消息(Message)函数,其功能在于基于边传递消息。具体实现如下:
def gcn_msg(edge):msg = edge.src['h'] * edge.src['norm']return {'m': msg}Edge UDFs 需要传入一个 edge 参数,其中 edge 有三个属性:src、dst、data,分别对应源节点特征、目标节点特征和边特征。
我们的 Message 函数,是从源节点向目标节点传递,所以只考虑源节点的特征。
节点中的 'norm' 用于归一化,具体计算方式后面会说。
对于每个节点来说,可能过会收到很多个源节点传过来的消息,所以可以将这些消息存储在邮箱中(mailbox)。
我们那再来定义一个聚合(Reduce)函数。
消息传递完后,每个节点都要处理下他们的“信箱”(mailbox),Reduce 函数的作用就是用来处理节点“信箱”的消息的。
Reduce 函数是一个 Node UDFs。
Node UDFs 接收一个 node 的参数,并且 node 有两个属性 data 和 mailbox,分别为节点的特征和用来接收信息的“信箱”。
def gcn_reduce(node):# 需要注意:消息存放在 mailbox 的第二个维上,第一维是消息的数量accum = torch.sum(node.mailbox['m'], dim=1) * node.data['norm']return {'h': accum}Messge UDF 作用于边上,而 Reduce UDF 作用于节点上。两者的关系如下:
从左到右开始看,源节点通过 message 函数传递节点特征,并传递到目标节点的 Mailbox 中,在触发 Node UDF 时(这里为 Reduce 函数),Mailbox 将被清空。
上图中我们还可以看到作用于节点的有两个函数:Apply 函数和 Reduce 函数。
Reduce 函数我们上面介绍过了,那这个 Apply 函数是什么呢?
Apply 函数为节点更新的函数,可以用于「初始化参数」和「对节点特征的进行非线形变换」。
初始化参数:我们刚刚指出,参数分布服从 Glorot 均匀分布,所以要给节点加偏置的话,我们也需要将其初始化为并使其服从 Glorot 均匀分布,如下面代码中的 reset_parameters 函数
非线形变换:GCN 中每一层进行传递后,节点可能需要进行非线形变换,如下面代码中 forward 函数
class NodeApplyModule(nn.Module):def __init__(self, out_feats, activation=None, bias=True):super(NodeApplyModule, self).__init__()if bias:self.bias = nn.Parameter(torch.Tensor(out_feats))else:self.bias = Noneself.activation = activationself.reset_parameters()def reset_parameters(self):if self.bias is not None:stdv = 1. / math.sqrt(self.bias.size(0))self.bias.data.uniform_(-stdv, stdv)def forward(self, nodes):h = nodes.data['h']if self.bias is not None:h = h + self.biasif self.activation:h = self.activation(h)return {'h': h}有了 Message 函数、Reduce 函数和节点的更新函数后,我们需要将其连贯起来:
g.update_all(message_func='default', reduce_func='default', apply_node_func='default') 这个函数可以用于发送信息并更新所有节点,是 send() 和 recv() 函数的一个简单组合
3.2.3 GCNLayer有了这些后,我们便可以定义 GCNLayer 了:
class GCNLayer(nn.Module):def __init__(self,g,in_feats,out_feats,activation,dropout,bias=True):super(GCNLayer, self).__init__()self.g = gself.weight = nn.Parameter(torch.Tensor(in_feats, out_feats))if dropout:self.dropout = nn.Dropout(p=dropout)else:self.dropout = 0.self.node_update = NodeApplyModule(out_feats, activation, bias)self.reset_parameters()def reset_parameters(self):stdv = 1. / math.sqrt(self.weight.size(1))self.weight.data.uniform_(-stdv, stdv)def forward(self, h):if self.dropout:h = self.dropout(h)self.g.ndata['h'] = torch.mm(h, self.weight)self.g.update_all(gcn_msg, gcn_reduce, self.node_update)h = self.g.ndata.pop('h')return h然后我们把 GCNLayer 拼接在一起组成 GCN 网络
class GCN(nn.Module):def __init__(self,g,in_feats,n_hidden,n_classes,n_layers,activation,dropout):super(GCN, self).__init__()self.layers = nn.ModuleList()# input layerself.layers.append(GCNLayer(g, in_feats, n_hidden, activation, dropout))# hidden layersfor i in range(n_layers - 1):self.layers.append(GCNLayer(g, n_hidden, n_hidden, activation, dropout))# output layerself.layers.append(GCNLayer(g, n_hidden, n_classes, None, dropout))def forward(self, features):h = featuresfor layer in self.layers:h = layer(h)return h3.3 Third versionDGL 的第三种写法:使用 DGL 的内置(builtin)函数
由于 Messge 和 Reduce 函数使用的比较频繁,所以 DGL 了内置函数以方便使用,我们把刚刚的 Message 和 Reduce 函数改变为内置函数有:
  • dgl.function.copy_src(src, out):Message 函数其实就是把源节点的特征拷贝到目标节点,所以可以换用内置的 copy_src 函数。

  • dgl.function.sum(msg, out):Reduce 函数其实就是聚合节点 Mailbox 中的消息,所以可以换用内置的 sum 函数。

class GCNLayer(nn.Module):def __init__(self,g,in_feats,out_feats,activation,dropout,bias=True):super(GCNLayer, self).__init__()self.g = gself.weight = nn.Parameter(torch.Tensor(in_feats, out_feats))if bias:self.bias = nn.Parameter(torch.Tensor(out_feats))else:self.bias = Noneself.activation = activationif dropout:self.dropout = nn.Dropout(p=dropout)else:self.dropout = 0.self.reset_parameters()def reset_parameters(self):stdv = 1. / math.sqrt(self.weight.size(1))self.weight.data.uniform_(-stdv, stdv)if self.bias is not None:self.bias.data.uniform_(-stdv, stdv)def forward(self, h):if self.dropout:h = self.dropout(h)h = torch.mm(h, self.weight)# normalization by square root of src degreeh = h * self.g.ndata['norm']self.g.ndata['h'] = hself.g.update_all(fn.copy_src(src='h', out='m'),fn.sum(msg='m', out='h'))h = self.g.ndata.pop('h')# normalization by square root of dst degreeh = h * self.g.ndata['norm']# biasif self.bias is not None:h = h + self.biasif self.activation:h = self.activation(h)return h
  • 这里的做了两次的标准化,对应 GCN 公式中的 

  • 这里把 Node 的 Apply 函数的功能合并到 GCNLayer 中了。

class GCN(nn.Module):def __init__(self,g,in_feats,n_hidden,n_classes,n_layers,activation,dropout):super(GCN, self).__init__()self.layers = nn.ModuleList()# input layerself.layers.append(GCNLayer(g, in_feats, n_hidden, activation, 0.))# hidden layersfor i in range(n_layers - 1):self.layers.append(GCNLayer(g, n_hidden, n_hidden, activation, dropout))# output layerself.layers.append(GCNLayer(g, n_hidden, n_classes, None, dropout))def forward(self, features):h = featuresfor layer in self.layers:h = layer(h)return h

训练

dropout=0.5
gpu=-1
lr=0.01
n_epochs=200
n_hidden=16  # 隐藏层节点的数量
n_layers=2  # 输入层 + 输出层的数量
weight_decay=5e-4  # 权重衰减
self_loop=True  # 自循环
# cora 数据集data = citegrh.load_cora()
features = torch.FloatTensor(data.features)
labels = torch.LongTensor(data.labels)
train_mask = torch.BoolTensor(data.train_mask)
val_mask = torch.BoolTensor(data.val_mask)
test_mask = torch.BoolTensor(data.test_mask)in_feats = features.shape[1]
n_classes = data.num_labels
n_edges = data.graph.number_of_edges()
# 构建 DGLGraph
g = data.graphif self_loop:g.remove_edges_from(nx.selfloop_edges(g))g.add_edges_from(zip(g.nodes(), g.nodes()))
g = DGLGraph(g)

这里大家可能会有些疑惑:为什么要先移除自环?然后再加上自环。

这个主要是为了防止原本数据集中有一部分的自环,如果不去掉直接加上自环的话,会导致一些节点有两个自环,而有些只有一个。

# 加载 GPU
if gpu < 0:cuda = False
else:cuda = Truetorch.cuda.set_device(gpu)features = features.cuda()labels = labels.cuda()train_mask = train_mask.cuda()val_mask = val_mask.cuda()test_mask = test_mask.cuda()
# 归一化,依据入度进行计算
degs = g.in_degrees().float()
norm = torch.pow(degs, -0.5)
norm[torch.isinf(norm)] = 0
if cuda:norm = norm.cuda()
g.ndata['norm'] = norm.unsqueeze(1)
# 创建一个 GCN 的模型,可以选择上面的任意一个进行初始化
model = GCN(g,in_feats,n_hidden,n_classes,n_layers,F.relu,dropout)if cuda:model.cuda()
# 采用交叉熵损失函数和 Adam 优化器
loss_fcn = torch.nn.CrossEntropyLoss()optimizer = torch.optim.Adam(model.parameters(),lr=lr,weight_decay=weight_decay)
# 定义一个评估函数
def evaluate(model, features, labels, mask):model.eval()with torch.no_grad():logits = model(features)logits = logits[mask]labels = labels[mask]_, indices = torch.max(logits, dim=1)correct = torch.sum(indices == labels)return correct.item() * 1.0 / len(labels)
# 训练,并评估
dur = []
for epoch in range(n_epochs):model.train()t0 = time.time()# forwardlogits = model(features)loss = loss_fcn(logits[train_mask], labels[train_mask])optimizer.zero_grad()loss.backward()optimizer.step()dur.append(time.time() - t0)if epoch % 10 == 0:acc = evaluate(model, features, labels, val_mask)print("Epoch {:05d} | Time(s) {:.4f} | Loss {:.4f} | Accuracy {:.4f} | ""ETputs(KTEPS) {:.2f}". format(epoch, np.mean(dur), loss.item(),acc, n_edges / np.mean(dur) / 1000))print()
acc = evaluate(model, features, labels, test_mask)
print("Test accuracy {:.2%}".format(acc))
Epoch 00000 | Time(s) 0.0178 | Loss 1.9446 | Accuracy 0.2100 | ETputs(KTEPS) 594.54
Epoch 00010 | Time(s) 0.0153 | Loss 1.7609 | Accuracy 0.3533 | ETputs(KTEPS) 689.33
Epoch 00020 | Time(s) 0.0150 | Loss 1.5518 | Accuracy 0.5633 | ETputs(KTEPS) 703.47
Epoch 00030 | Time(s) 0.0146 | Loss 1.2769 | Accuracy 0.5867 | ETputs(KTEPS) 721.28
Epoch 00040 | Time(s) 0.0143 | Loss 1.0785 | Accuracy 0.6567 | ETputs(KTEPS) 740.36
Epoch 00050 | Time(s) 0.0140 | Loss 0.8881 | Accuracy 0.7067 | ETputs(KTEPS) 754.21
Epoch 00060 | Time(s) 0.0138 | Loss 0.6994 | Accuracy 0.7533 | ETputs(KTEPS) 763.21
Epoch 00070 | Time(s) 0.0137 | Loss 0.6249 | Accuracy 0.7800 | ETputs(KTEPS) 770.54
Epoch 00080 | Time(s) 0.0137 | Loss 0.5048 | Accuracy 0.7800 | ETputs(KTEPS) 772.31
Epoch 00090 | Time(s) 0.0136 | Loss 0.4457 | Accuracy 0.7867 | ETputs(KTEPS) 778.78
Epoch 00100 | Time(s) 0.0135 | Loss 0.4167 | Accuracy 0.7800 | ETputs(KTEPS) 782.25
Epoch 00110 | Time(s) 0.0134 | Loss 0.3389 | Accuracy 0.8000 | ETputs(KTEPS) 786.52
Epoch 00120 | Time(s) 0.0134 | Loss 0.3777 | Accuracy 0.8100 | ETputs(KTEPS) 789.85
Epoch 00130 | Time(s) 0.0133 | Loss 0.3307 | Accuracy 0.8133 | ETputs(KTEPS) 792.00
Epoch 00140 | Time(s) 0.0133 | Loss 0.2542 | Accuracy 0.7933 | ETputs(KTEPS) 794.13
Epoch 00150 | Time(s) 0.0133 | Loss 0.2937 | Accuracy 0.8000 | ETputs(KTEPS) 795.73
Epoch 00160 | Time(s) 0.0132 | Loss 0.2944 | Accuracy 0.8333 | ETputs(KTEPS) 797.04
Epoch 00170 | Time(s) 0.0132 | Loss 0.2161 | Accuracy 0.8167 | ETputs(KTEPS) 799.74
Epoch 00180 | Time(s) 0.0132 | Loss 0.1972 | Accuracy 0.8200 | ETputs(KTEPS) 801.31
Epoch 00190 | Time(s) 0.0131 | Loss 0.2339 | Accuracy 0.8167 | ETputs(KTEPS) 802.92
Test accuracy 80.40%
5.结论以上便是本教程的全部,当然还有其他实现的方法,比如说,直接利用矩阵相乘来进行迭代。
参考目录
DGL Github
DGL 官方文档
《深度学习——Xavier初始化方法》
《DGL 作者答疑!关于 DGL 你想知道的都在这里-周金晶》推荐阅读
  • 利用 AssemblyAI 在 PyTorch 中建立端到端的语音识别模型

  • 京东姚霆:推理能力,正是多模态技术未来亟需突破的瓶颈

  • 性能超越最新序列推荐模型,华为诺亚方舟提出记忆增强的图神经网络

  • FPGA 无解漏洞 “StarBleed”轰动一时,今天来扒一下技术细节!

  • 真惨!连各大编程语言都摆起地摊了

  • 发送0.55 ETH花费近260万美元!这笔神秘交易引发大猜想

你点的每个“在看”,我都认真当成了AI

相关文章:

CentOS5快速搭建vsftp服务

既然强调快速, 我们就马上开始&#xff0c;环境是centos5安装vsftpd&#xff0c;用了半天做了测试与修改&#xff0c;终于完成。 第一步&#xff1a;安装vsftpd&#xff0c;在终端允许 # yum -y install vsftpd 没什么问题就直接安装好啦 第二步&#xff1a;编辑vsftpd的配置…

我和freelancer不得不说的故事5 --- 心理落差

我和freelancer不得不说的故事5 --- 心理落差 我下海之前所在的外企&#xff0c;是一家顶级知名IT企业&#xff0c;其SAP咨询服务业务规模和影响都很大&#xff0c;是SAP咨询界五大咨询公司之一。我从07年加入这家公司&#xff0c;到辞职下海&#xff0c;在这家公司工作8年半。…

一起谈.NET技术,asp.net控件开发基础(18)

本篇继续上篇的讨论&#xff0c;可能大家已经在使用asp.net2.0了,DataSource属性不再使用,而是跟数据源控件搭配使用.现在讨论的绑定技术都是基于1.1版本,先熟悉一下,本质上是一样的,这样一步步的学习.对以后绝对有帮助.因为当你使用数据源控件,只需要设置一个DataSourceID,方便…

使用sqlserver来存放和取得session

asp.net 提供了三种存放 session的方式。 1 InProc 2 State Server 3 SQL Server 第一种是我们经常用的&#xff0c;第2中就是使用一个名为 state server 的机器用它的内存来存放其他机器的session 状态&#xff0c;其实&#xff0c;我们还可以在 sql server 里面来存放和取…

五项挑战获四项第一,地平线霸榜Waymo自动驾驶算法挑战赛

美国当地时间6月15日&#xff0c;Alphabet&#xff08;Google母公司&#xff09;旗下的自动驾驶公司Waymo在CVPR 2020自动驾驶Workshop上揭晓Waymo开放数据集挑战赛的结果&#xff0c;边缘AI芯片企业地平线斩获5项挑战中的4项全球第一。 本次挑战赛&#xff0c;Waymo开放了其自…

SSO单点登录基于CAS架构封装 Memcached 实例

2019独角兽企业重金招聘Python工程师标准>>> SSO认证中心是CAS整个应用架构的一个极其重要的关键点&#xff0c;必须满足如下两点要求&#xff1a; 1.高可用&#xff0c;不允许程序发生故障。如果认证中心发生故障&#xff0c;整个应用群将无法登录&#xff0c;导致…

HTMLButton控件下的Confirm()

作者&#xff1a;未知 请作者速与本人联系一、前言在ASP.NET中大部分如删除等一些动作为了友好都为添加confirm()来弹出消息框进行提示&#xff0c;但是HTML控件和WEB控件是否使用的方法是一样的呢?二、方法A. System.Web.UI.WebControls.Button控件现在一般都是这样在Page_…

Python 还能实现哪些 AI 游戏?附上代码一起来一把!

作者 | 李秋键责编 | Carol头图 | CSDN 付费下载自视觉中国人工智能作为当前热门在我们生活中得到了广泛应用&#xff0c;尤其是在智能游戏方面&#xff0c;有的已经达到了可以和职业选手匹敌的效果。而DQN算法作为智能游戏的经典选择算法&#xff0c;其主要是通过奖励惩罚机制…

一起谈.NET技术,专访微软MVP衣明志:走进ASP.NET MVC 2框架开发

日前微软已经发布ASP.NET MVC 2框架RC版&#xff0c;究竟这次RC版本的发布对于WEB开发者带来怎样的改变&#xff1f;以及未来ASP.NET MVC 2正式版还会有哪些改进&#xff1f;带着这样的问题&#xff0c;我们51CTO记者彭凡专门采访了微软MVP衣明志老师。ASP.NET MVC是微软官方提…

Entity Framework:Code-First Tutorial开篇

这个系列文章是关于Entity Framework Code-First的英文系列文章&#xff0c;内容不错&#xff0c;每篇一个主题知识点介绍&#xff0c;特转载过来 原文地址&#xff1a;http://www.entityframeworktutorial.net/code-first/entity-framework-code-first.aspx转载于:https://www…

Android开发者指南(22) —— Accessing Resources

前言   本章内容为Android开发者指南的Framework Topics/Application Resources/Accessing Resources章节&#xff0c;译为"资源调用"&#xff0c;版本为Android 3.2 r1&#xff0c;翻译来自&#xff1a;"CodeGuy"&#xff0c;欢迎访问他的博客&#xff…

如何快速实现HTML编辑器.NET组件

作者&#xff1a;未知 请作者速与本人联系得到“素材”首先我们需要得到一个HTML编辑器的原始代码&#xff0c;网上有不少这类的编辑器&#xff0c;如大名鼎鼎的RichTextBox&#xff0c;为了避免版权纠纷&#xff0c;以我所做得为例&#xff08;暂名&#xff1a;UltraTextBox…

罗永浩力荐,丁磊豪送的学习神器:手机查词真不如这支AI词典笔?

销量确实称得上火爆。尽管999元的直播优惠价价格并不低&#xff0c;但这支有道词典笔专业版在快手直播间还是经历了返场&#xff0c;最终20000多台一抢而空。 为这款产品站台的正是网易CEO丁磊&#xff0c;6月11日是他网上卖货的首秀&#xff0c;不过更重要的是&#xff0c;那天…

Thinking in java中关于Exception的一道面试题.

今天看到Thinking in Java中一个关于Exception的例子:最后看到有一篇总结的比较好的文章, 这里拿来记录下, 文章地址是:http://blog.csdn.net/salerzhang/article/details/46581457 感谢原作者. 1 class Annoyance extends Exception {}2 class Sneeze extends Annoyance {}3 …

使用 .NET 框架轻松开发完美的 Web 窗体控件

作者&#xff1a;David S. Platt 出自&#xff1a;微软 本文假定您熟悉 Visual Basic .NET、C# 和 HTML 下载本文的代码&#xff1a; WebC.exe (274KB) 摘要 预建的自定义控件可以简化和加快应用程序的设计&#xff0c;并使您能够维护 UI 的一致性。但是&#xff0c;预先打…

史上最强女游戏程序员

也许你听说过John Carmack 和Tim Sweeney等大牛的名字&#xff0c;而向来游戏工业都是阳盛阴衰&#xff0c;适逢国际妇女节&#xff0c;今天我为大家介绍游戏业界一位史上最强女游戏程序员&#xff1a;Corrinne Yu。 简历 以下是她在游戏业界内的简历 微软Halo团队首席引擎架构…

重磅日程公布!与百名大咖在线交流技术,2天20个AI论坛不可错过

当全球都在面向 AI 变革时&#xff0c;AI 不再是触不可及&#xff0c;它需要产业化落地&#xff0c;为社会创造价值。在这一轮技术革命、技术浪潮中&#xff0c;开发者们成为构建任何一家AI企业的核心竞争力。不过&#xff0c;不同于此前只懂开发语言、数据结构便可轻松躲过新技…

Python取出列表相应值的位置(表处理)

#需求在一个列表中&#xff0c;取出相应值的位置方法1&#xff1a;#脚本示例[rootlocalhost opt]# cat list.py #!/usr/bin/env python #_*_ coding:utf-8 _*_ name[!,#,*,Eric,wsyht,jack,jack,a,b,c,d,1,2,3,4,5,6,1,2,3,4,5,6,1,2,3,4,5,6,2332,4,2,6,2] first_pos 0 for …

rhel5.5安装xwindow

rhel5.5安装xwindow 1安装xwindow yum groupinstall "X Window System" 2、安装GNOME桌面环境 yum groupinstall "GNOME Desktop Environment" 3、卸载GNOME桌面环境 yum groupremove "GNOME Desktop Environment"转载于:https://blog.51cto…

使用 ASP.NET 加密口令

作者&#xff1a;未知 请作者速与本人联系当我们在网站上建立数据库时&#xff0c;保护用户的信息安全是非常必要的。多数用户不愿意让别人知道自己的信息&#xff0c;同时网管也不想因为安全问题而丢失网站的信誉。无论对于谁&#xff0c;安全问题都是非常重要的。为了解决这…

算法鼻祖高德纳,82 岁仍在写《计算机程序设计的艺术》

作者 | 年素清编辑 | 伍杏玲出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;高德纳&#xff08;Donald Ervin Knuth&#xff09;被誉为现代计算机科学的鼻祖&#xff0c;毕生致力于编译程序、属性文法和运算法则等领域的前沿研究&#xff0c;共出版专著17部&#x…

centos查看特定程序占用端口情况

ps axu |grep 程序名&#xff0c;找到特定程序的pidnetstat -nltp |grep pid即可。转载于:https://blog.51cto.com/zhukeqiang/1811735

关于页面刷新的问题

在做.net开发时&#xff0c;经常能碰到这样的情况&#xff0c;页面很长&#xff0c;而我们一般用的都是服务器端控件&#xff0c;用服务器端控件有这样一个缺点&#xff0c;就是控件每次都要和服务器交互&#xff0c;而产生页面的刷新&#xff0c;试想一下&#xff0c;如果页面…

技术直播:程序员副业的修炼指南!(限免报名)

面试造飞机&#xff0c;上班拧螺丝&#xff0c;每天想辞职&#xff0c;但无奈副业还“大器晚成”的样子&#xff01;那可能是你还没有选对副业&#xff01;滴滴 ~福利卡&#xff01;&#xff01;&#xff01;CSDN学院邀请汤小洋老师开设技术直播课《程序员副业之路-三大终极秘籍…

Linux 双网卡绑定测试

Linux 双网卡绑定测试 先介绍一下情况&#xff0c;服务器A和服务器B都是CentOS 4.6的系统&#xff0c;现在要做HA Cluster&#xff0c;为了避免裂脑的发生&#xff0c;要提高心跳链路的可靠性&#xff0c;下图是现时的连接情况&#xff0c;服务器A的eth2、eth3分别和服务器B的e…

第六章练习题和知识面扩充

作业题&#xff1a;1. 自动获取IP地址的命令是什么&#xff1f;您知道在什么情况下&#xff0c;您的Linux才可以自动获取IP地址&#xff1f;2. 远程连接Linux服务器&#xff0c;需要Linux服务器开启sshd服务&#xff0c;那么sshd服务默认监听哪个端口&#xff1f;这个端口是否可…

一文详解面向多级多模态场景的召回引擎

作者| 阿里文娱开发专家 崇懿、阿里文娱开发专家慧善责编 | 屠敏头图 | CSDN 下载自视觉中国出品 | CSDN&#xff08;ID&#xff1a;CSDNnews&#xff09;优酷视频搜索在文本搜索系统的基础上&#xff0c;不断探索视频搜索的方案&#xff0c;在多模态输入、多级多模态索引、跨模…

对比.Net PetShop和Duwamish来探讨Ado.Net的数据库编程模式

作者:卢彦.NET PetShop和Duwamish简单介绍相信大家一定听说过有名的"宠物店大战"&#xff0c;没错&#xff0c;本文的主角之一就是获胜方.NET PetShop&#xff0c;微软号称以27倍的速度和1/4的代码量遥遥领先于基于J2EE的PetStore宠物商店。虽然SUN也曾对此抱怨过不满…

如何直接将HTML代码加载到TWebBrowser

wbRecvContent//为 webbrowser控件 procedure TFrmMain.ShowHtmlCentent(slt: TStrings); var aMemory: TMemoryStream; pbuf: PAnsiChar; begin aMemory : TMemoryStream.Create(); try aMemory.Clear; slt.SaveToStream(aMemory); aMemory.Seek(0, soBeginning); wbRecvConte…

JavaScript基础(一) 数据类型

动态类型 JavaScript 是一种弱类型或者说动态语言。这意味着你不用提前声明变量的类型&#xff0c;在程序运行过程中&#xff0c;类型会被自动确定。 数据类型 最新的 ECMAScript 标准定义了 7 种数据类型: 6 种 原始类型: BooleanNullUndefinedNumberStringSymbol (ECMAScript…