技术标签: 人工智能
人工智能AI:Keras PyTorch MXNet TensorFlow PaddlePaddle 深度学习实战(不定时更新)
下面我们主要以一些常见的网络结构去解析,并介绍大部分的网络的特点。这里看一下卷积的发展历史图。
首先我们从一个稍微早一些的卷积网络结构LeNet-5(这里稍微改了下名字),开始的目的是用来识别数字的。从前往后介绍完整的结构组成,并计算相关输入和输出。
shape | size | parameters | |
---|---|---|---|
Input | (32,32,3) | 3072 | 0 |
Conv1(f=5,s=1) | (28,28,6) | 4704 | 450+6 |
Pool1 | (14,14,6) | 1176 | 0 |
Conv2(f=5,s=1) | (10,10,16) | 1600 | 2400+16 |
Pool2 | (5,5,16) | 400 | 0 |
FC3 | (120,1) | 120 | 48000+120 |
FC4 | (84,1) | 84 | 10080+84 |
Ouput:softmax | (10,1) | 10 | 840+10 |
事实上,在过去很多年,许多机构或者学者都发布了各种各样的网络,其实去了解设计网络最好的办法就是去研究现有的网络结构或者论文。大多数网络设计出来是为了Image Net的比赛(解决ImageNet中的1000类图像分类或定位问题),后来大家在各个业务上进行使用。
2012年,Alex Krizhevsky、Ilya Sutskever在多伦多大学Geoff Hinton的实验室设计出了一个深层的卷积神经网络AlexNet,夺得了2012年ImageNet LSVRC的冠军,且准确率远超第二名(top5错误率为15.3%,第二名为26.2%),引起了很大的轰动。AlexNet可以说是具有历史意义的一个网络结构。
3.4.3.1 常见结构特点
整个过程:AlexNet-->NIN-->(VGG—GoogLeNet)-->ResNet-->DenseNet
GoogleNet,2014年比赛冠军的model,这个model证明了一件事:用更多的卷积,更深的层次可以得到更好的结构。(当然,它并没有证明浅的层次不能达到这样的效果)
特点:
闪光点:
卷积层使用更小的filter尺寸和间隔
与AlexNet相比,可以看出VGG-Nets的卷积核尺寸还是很小的,比如AlexNet第一层的卷积层用到的卷积核尺寸就是11*11,这是一个很大卷积核了。而反观VGG-Nets,用到的卷积核的尺寸无非都是1×1和3×3的小卷积核,可以替代大的filter尺寸。
3×3卷积核的优点:
VGG-16的Keras实现(这里只做展示了解):
def VGG_16():
model = Sequential()
model.add(Conv2D(64,(3,3),strides=(1,1),input_shape=(224,224,3),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(64,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(128,(3,2),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(128,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(256,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(Conv2D(512,(3,3),strides=(1,1),padding='same',activation='relu',kernel_initializer='uniform'))
model.add(MaxPooling2D(pool_size=(2,2)))
model.add(Flatten())
model.add(Dense(4096,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(4096,activation='relu'))
model.add(Dropout(0.5))
model.add(Dense(1000,activation='softmax'))
return model
首先我们要说一下在Network in Network中引入的1 x 1卷积结构的相关作用,下面我们来看1*1卷积核的优点:
3.4.4.1MLP卷积(1 x 1卷积)
从图中,看到1 x 1卷积的过程,那么这里先假设只有3个1x1Filter,那么最终结果还是56x56x3。但是每一个FIlter的三个参数的作用
我们甚至可以把这几个FIlter可以看成就是一个简单的神经元结构,每个神经元参数数量与前面的通道数量相等。
那么对于1x1网络对通道数的变化,其实并不是最重要的特点,因为毕竟3 x 3,5 x 5都可以带来通道数的变化,
而1x1卷积的参数并不多,我们拿下面的例子来看。
对于单通道输入,1×1的卷积确实不能起到降维作用,但对于多通道输入,就不不同了。假设你有256个特征输入,256个特征输出,同时假设Inception层只执行3×3的卷积。这意味着总共要进行 256×256×3×3的卷积(589000次乘积累加(MAC)运算)。这可能超出了我们的计算预算,比方说,在Google服务器上花0.5毫秒运行该层。作为替代,我们决定减少需要卷积的特征的数量,比如减少到64(256/4)个。在这种情况下,我们首先进行256到64的1×1卷积,然后在所有Inception的分支上进行64次卷积,接着再使用一个64到256的1×1卷积。
现在的计算量大约是70000(即16000+36000+16000),相比之前的约600000,几乎减少了10倍。这就通过小卷积核实现了降维。
现在再考虑一个问题:为什么一定要用1×1卷积核,3×3不也可以吗?考虑[50,200,200]的矩阵输入,我们可以使用20个1×1的卷积核进行卷积,得到输出[20,200,200]。有人问,我用20个3×3的卷积核不是也能得到[20,200,200]的矩阵输出吗,为什么就使用1×1的卷积核?我们计算一下卷积参数就知道了,对于1×1的参数总数:20×200×200×(1×1),对于3×3的参数总数:20×200×200×(3×3),可以看出,使用1×1的参数总数仅为3×3的总数的九分之一!所以我们使用的是1×1卷积核。
这个结构其实还有名字叫盗梦空间结构。
目的:
Inception Module. 使用Inception Module的模块的GoogleNet不仅比Alex深,而且参数比AlexNet足足减少了12倍。
GoogleNet作者的初始想法是用多个不同类型的卷积核代替一个3x3的小卷积核(如左图),好处是可以使提取出来的特征具有多样化,并且特征之间的co-relationship不会很大,最后用把feature map都concatenate起来使网络做得很宽,然后堆叠Inception Module将网络变深。但仅仅简单这么做会使一层的计算量爆炸式增长,如下图,854M 操作:
计算量主要来自于输入前的feature map的维度256,和1x1卷积核的输出维度:192。那可否先使用1x1卷积核将输入图片的feature map维度先降低,进行信息压缩,在使用3x3卷积核进行特征提取运算?线图就只有358M操作
3x3
的卷积核。256 维的输入直接经过一个 3×3×256
的卷积层,输出一个 256 维的 feature map ,那么参数量为:256×3×3×256 = 589,824 。1x1
的卷积核,再经过 3x3
卷积核,最后经过一个 1x1
卷积核。 256 维的输入先经过一个 1×1×64
的卷积层,再经过一个 3x3x64
的卷积层,最后经过 1x1x256
的卷积层,则总参数量为:256×1×1×64 + 64×3×3×64 + 64×1×1×256 = 69,632 。其中包含了多个Inception结构。
完整结构:
keras代码实现:
def Conv2d_BN(x, nb_filter,kernel_size, padding='same',strides=(1,1),name=None):
if name is not None:
bn_name = name + '_bn'
conv_name = name + '_conv'
else:
bn_name = None
conv_name = None
x = Conv2D(nb_filter,kernel_size,padding=padding,strides=strides,activation='relu',name=conv_name)(x)
x = BatchNormalization(axis=3,name=bn_name)(x)
return x
def Inception(x,nb_filter):
branch1x1 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)
branch3x3 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)
branch3x3 = Conv2d_BN(branch3x3,nb_filter,(3,3), padding='same',strides=(1,1),name=None)
branch5x5 = Conv2d_BN(x,nb_filter,(1,1), padding='same',strides=(1,1),name=None)
branch5x5 = Conv2d_BN(branch5x5,nb_filter,(1,1), padding='same',strides=(1,1),name=None)
branchpool = MaxPooling2D(pool_size=(3,3),strides=(1,1),padding='same')(x)
branchpool = Conv2d_BN(branchpool,nb_filter,(1,1),padding='same',strides=(1,1),name=None)
x = concatenate([branch1x1,branch3x3,branch5x5,branchpool],axis=3)
return x
def GoogLeNet():
inpt = Input(shape=(224,224,3))
#padding = 'same',填充为(步长-1)/2,还可以用ZeroPadding2D((3,3))
x = Conv2d_BN(inpt,64,(7,7),strides=(2,2),padding='same')
x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
x = Conv2d_BN(x,192,(3,3),strides=(1,1),padding='same')
x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
x = Inception(x,64)#256
x = Inception(x,120)#480
x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
x = Inception(x,128)#512
x = Inception(x,128)
x = Inception(x,128)
x = Inception(x,132)#528
x = Inception(x,208)#832
x = MaxPooling2D(pool_size=(3,3),strides=(2,2),padding='same')(x)
x = Inception(x,208)
x = Inception(x,256)#1024
x = AveragePooling2D(pool_size=(7,7),strides=(7,7),padding='same')(x)
x = Dropout(0.4)(x)
x = Dense(1000,activation='relu')(x)
x = Dense(1000,activation='softmax')(x)
model = Model(inpt,x,name='inception')
return model
Inception v2的论文,提出”Batch Normalization”思想:消除因conv而产生internal covariant shift, 保持数据在训练过程中的统计特性的一致性。Inception v2的结构是在卷积层与激活函数之间插入BN层:conv-bn-relu
3.4.4.8 Inception v3
问题:就算有了Pointwise Conv,由于 5x5 和 7x7 卷积核直接计算参数量还是非常大,训练时间还是比较长,我们还要再优化。
特点:Inception v3使用factorization的方法进一步对inception v2的参数数量进行优化和降低.
2、Spatial Factorization into Asymmetric(不对称) Convolution:使用receptive field等效的原理,进一步将nxn的卷积核裂化成1xn和nx1的卷积核串联形式或者并联形式,进一步减少计算量。
Inception v4是将Inception module和residual module结合起来。引入了ResNet,使训练加速,性能提升。(后面会介绍ResNet)
Xception的目标是设计出易迁移、计算量小、能适应不同任务,且精度较高的模型。那么Xception与Inception-v3在结构上有什么差别呢?
2、依据depthwise separable convolution的思想,可以进一步把上图改造成下图.将每个channel上的空间关联分别使用一个相应的conv 3x3来单独处理呢。如此就得到了Separable conv。
什么是depthwise separable convolution(深度可分离卷积)呢?
1、对于112×112×64的输入做一次普通的3×3卷积,每个卷积核大小为3×3×64,也就是说,每一小步的卷积操作是同时在面上(3×3的面)和深度上(×64的深度)进行的
它就是由Inception v3直接演变而来,并且引入了Residual learning的结构
总结:最后,把这7个3×3的卷积的输出叠在一起就可以了。根据Xception论文的实验结果,Xception在精度上略低于Inception-v3,但在计算量和迁移性上都好于Inception-v3。
3.4.4.11 Inception Module 总结
Resnet通过引入残差单元来解决退化问题。
结构:
(1)通过增加 恒等快捷连接(identity shortcut connection)实现,直接跳过一个或多个层。优势是残差映射在实际中往往更容易优化。
(2)Resnet网络中短路连接shortcut的方式考虑到x的维度与F(X)维度可能不匹配情况,需进行维度匹配。通常采用两种方法解决这一问题:
zero_padding:对恒等层进行0填充的方式将维度补充完整。这种方法不会增加额外的参数
projection:在恒等层采用1x1的卷积核来增加维度。这种方法会增加额外的参数
(3)bottleneck 实现方式
使用1x1的卷积层来降维,最后一层使用1x1的卷积层来进行升维
(4)激活函数移动到残差部分可以提高模型的精度
2015年何恺明推出的ResNet在ISLVRC和COCO上横扫所有选手,获得冠军。ResNet在网络结构上做了大创新,而不再是简单的堆积层数,ResNet在卷积神经网络的新思路,绝对是深度学习发展历程上里程碑式的事件。
引入:
对神经网络模型添加新的层,充分训练后的模型是否只可能更有效地降低训练误差?理论上,原模型解的空间只是新模型解的空间的子空间。新模型和原模型将同样有效。由于新模型可能得出更优的解来拟合训练数据集,因此添加层似乎更容易降低训练误差。然而在实践中,添加过多的层后训练误差往往不降反升。
3.4.5.1 残差网络-Residual network
通过增加 恒等快捷连接(identity shortcut connection)实现,直接跳过一个或多个层。
1、理解:假设我们希望学出的理想映射为f(x),从而作为图5.9上方激活函数的输入。左图虚线框中的部分需要直接拟合出该映射f(x),而右图虚线框中的部分则需要拟合出有关恒等映射的残差映射f(x)−x
我们以该图来解释:
考虑到x的维度与F(X)维度可能不匹配情况,需进行维度匹配。这里论文中采用两种方法解决这一问题(其实是三种,但通过实验发现第三种方法会使performance急剧下降,故不采用):
当研究50层以上的深层网络时,使用了上图所示的Bottleneck网络结构。该结构第一层使用1x1的卷积层来降维,最后一层使用1x1的卷积层来进行升维,从而保持与原来输入同维以便于恒等映射。
几种情况图示
构建了一个18层和34层的plain网络作为对比,所有层都只作简单的叠加,之后又构建了一个18层和34层的residual网络,在plain网络上加入shortcut,两个网络的参数量和计算量相同
在plain上观测到明显的退化现象,而在ResNet上没有出现退化现象,34层的网络反而比18层的更好,同时ResNet的收敛速度比plain要快。
ResNet比起VGG19这样的网络深很多,但是运算量是少于VGG19等的。
3.4.5.4 代码实现
def Conv2D_BN(x, filters, kernel_size, strides=(1, 1), padding='same', name=None):
if name:
bn_name = name + '_bn'
conv_name = name + '_conv'
else:
bn_name = None
conv_name = None
x = Conv2D(filters, kernel_size, strides=strides, padding=padding, activation='relu', name=conv_name)(x)
x = BatchNormalization(name=bn_name)(x)
return x
def identity_block(input_tensor, filters, kernel_size, strides=(1, 1), is_conv_shortcuts=False):
"""
:param input_tensor:
:param filters:
:param kernel_size:
:param strides:
:param is_conv_shortcuts: 直接连接或者投影连接
:return:
"""
x = Conv2D_BN(input_tensor, filters, kernel_size, strides=strides, padding='same')
x = Conv2D_BN(x, filters, kernel_size, padding='same')
if is_conv_shortcuts:
shortcut = Conv2D_BN(input_tensor, filters, kernel_size, strides=strides, padding='same')
x = add([x, shortcut])
else:
x = add([x, input_tensor])
return x
def bottleneck_block(input_tensor, filters=(64, 64, 256), strides=(1, 1), is_conv_shortcuts=False):
"""
:param input_tensor:
:param filters:
:param strides:
:param is_conv_shortcuts: 直接连接或者投影连接
:return:
"""
filters_1, filters_2, filters_3 = filters
x = Conv2D_BN(input_tensor, filters=filters_1, kernel_size=(1, 1), strides=strides, padding='same')
x = Conv2D_BN(x, filters=filters_2, kernel_size=(3, 3))
x = Conv2D_BN(x, filters=filters_3, kernel_size=(1, 1))
if is_conv_shortcuts:
short_cut = Conv2D_BN(input_tensor, filters=filters_3, kernel_size=(1, 1), strides=strides)
x = add([x, short_cut])
else:
x = add([x, input_tensor])
return x
def ResNet34(input_shape=(224, 224, 3), n_classes=1000):
"""
:param input_shape:
:param n_classes:
:return:
"""
input_layer = Input(shape=input_shape)
x = ZeroPadding2D((3, 3))(input_layer)
# block1
x = Conv2D_BN(x, filters=64, kernel_size=(7, 7), strides=(2, 2), padding='valid')
x = MaxPooling2D(pool_size=(3, 3), strides=2, padding='same')(x)
# block2
x = identity_block(x, filters=64, kernel_size=(3, 3))
x = identity_block(x, filters=64, kernel_size=(3, 3))
x = identity_block(x, filters=64, kernel_size=(3, 3))
# block3
x = identity_block(x, filters=128, kernel_size=(3, 3), strides=(2, 2), is_conv_shortcuts=True)
x = identity_block(x, filters=128, kernel_size=(3, 3))
x = identity_block(x, filters=128, kernel_size=(3, 3))
x = identity_block(x, filters=128, kernel_size=(3, 3))
# block4
x = identity_block(x, filters=256, kernel_size=(3, 3), strides=(2, 2), is_conv_shortcuts=True)
x = identity_block(x, filters=256, kernel_size=(3, 3))
x = identity_block(x, filters=256, kernel_size=(3, 3))
x = identity_block(x, filters=256, kernel_size=(3, 3))
x = identity_block(x, filters=256, kernel_size=(3, 3))
x = identity_block(x, filters=256, kernel_size=(3, 3))
# block5
x = identity_block(x, filters=512, kernel_size=(3, 3), strides=(2, 2), is_conv_shortcuts=True)
x = identity_block(x, filters=512, kernel_size=(3, 3))
x = identity_block(x, filters=512, kernel_size=(3, 3))
x = AveragePooling2D(pool_size=(7, 7))(x)
x = Flatten()(x)
x = Dense(n_classes, activation='softmax')(x)
model = Model(inputs=input_layer, outputs=x)
return model
def ResNet50(input_shape=(224, 224, 3), n_classes=1000):
"""
:param input_shape:
:param n_classes:
:return:
"""
input_layer = Input(shape=input_shape)
x = ZeroPadding2D((3, 3))(input_layer)
# block1
x = Conv2D_BN(x, filters=64, kernel_size=(7, 7), strides=(2, 2), padding='valid')
x = MaxPooling2D(pool_size=(3, 3), strides=(2, 2), padding='same')(x)
# block2
x = bottleneck_block(x, filters=(64, 64, 256), strides=(1, 1), is_conv_shortcuts=True)
x = bottleneck_block(x, filters=(64, 64, 256))
x = bottleneck_block(x, filters=(64, 64, 256))
# block3
x = bottleneck_block(x, filters=(128, 128, 512), strides=(2, 2), is_conv_shortcuts=True)
x = bottleneck_block(x, filters=(128, 128, 512))
x = bottleneck_block(x, filters=(128, 128, 512))
x = bottleneck_block(x, filters=(128, 128, 512))
# block4
x = bottleneck_block(x, filters=(256, 256, 1024), strides=(2, 2), is_conv_shortcuts=True)
x = bottleneck_block(x, filters=(256, 256, 1024))
x = bottleneck_block(x, filters=(256, 256, 1024))
x = bottleneck_block(x, filters=(256, 256, 1024))
x = bottleneck_block(x, filters=(256, 256, 1024))
x = bottleneck_block(x, filters=(256, 256, 1024))
# block5
x = bottleneck_block(x, filters=(512, 512, 2048), strides=(2, 2), is_conv_shortcuts=True)
x = bottleneck_block(x, filters=(512, 512, 2048))
x = bottleneck_block(x, filters=(512, 512, 2048))
x = AveragePooling2D(pool_size=(7, 7))(x)
x = Flatten()(x)
x = Dense(n_classes, activation='softmax')(x)
model = Model(inputs=input_layer, outputs=x)
return model
DenseNet是在ResNet之后的一个分类网络,连接方式的改变,使其在各大数据集上取得比ResNet更好的效果.。DenseNet的另一大特色是通过特征在channel上的连接来实现特征重用(feature reuse)。这些特点让DenseNet在参数和计算成本更少的情形下实现比ResNet更优的性能,DenseNet也因此斩获CVPR 2017的最佳论文奖。
3.4.6.1 设计理念
我们常用的有 GoogleNet、VGGNet、ResNet 模型,但随着网络层数的加深,网络在训练过程中的前传信号和梯度信号在经过很多层之后可能会逐渐消失。
DenseNet的网络结构主要由DenseBlock和Transition组成
DenseNet的优势主要体现在以下几个方面:
由于密集连接方式,DenseNet提升了梯度的反向传播,使得网络更容易训练。由于每层可以直达最后的误差信号,实现了隐式的“deep supervision”;
在ImageNet数据集上ResNet vs DenseNet
我们肯定会有疑问真个深度的卷积网络到底在学习什么?可以将网络学习过程中产生的特征图可视化出来,并且对比原图来看看每一层都干了什么。
文章浏览阅读1k次。通过使用ajax方法跨域请求是浏览器所不允许的,浏览器出于安全考虑是禁止的。警告信息如下:不过jQuery对跨域问题也有解决方案,使用jsonp的方式解决,方法如下:$.ajax({ async:false, url: 'http://www.mysite.com/demo.do', // 跨域URL ty..._nginx不停的xhr
文章浏览阅读2k次。关于在 Oracle 中配置 extproc 以访问 ST_Geometry,也就是我们所说的 使用空间SQL 的方法,官方文档链接如下。http://desktop.arcgis.com/zh-cn/arcmap/latest/manage-data/gdbs-in-oracle/configure-oracle-extproc.htm其实简单总结一下,主要就分为以下几个步骤。..._extproc
文章浏览阅读1.5w次。linux下没有上面的两个函数,需要使用函数 mbstowcs和wcstombsmbstowcs将多字节编码转换为宽字节编码wcstombs将宽字节编码转换为多字节编码这两个函数,转换过程中受到系统编码类型的影响,需要通过设置来设定转换前和转换后的编码类型。通过函数setlocale进行系统编码的设置。linux下输入命名locale -a查看系统支持的编码_linux c++ gbk->utf8
文章浏览阅读750次。今天准备从生产库向测试库进行数据导入,结果在imp导入的时候遇到“ IMP-00009:导出文件异常结束” 错误,google一下,发现可能有如下原因导致imp的数据太大,没有写buffer和commit两个数据库字符集不同从低版本exp的dmp文件,向高版本imp导出的dmp文件出错传输dmp文件时,文件损坏解决办法:imp时指定..._imp-00009导出文件异常结束
文章浏览阅读143次。当下是一个大数据的时代,各个行业都离不开数据的支持。因此,网络爬虫就应运而生。网络爬虫当下最为火热的是Python,Python开发爬虫相对简单,而且功能库相当完善,力压众多开发语言。本次教程我们爬取前程无忧的招聘信息来分析Python程序员需要掌握那些编程技术。首先在谷歌浏览器打开前程无忧的首页,按F12打开浏览器的开发者工具。浏览器开发者工具是用于捕捉网站的请求信息,通过分析请求信息可以了解请..._初级python程序员能力要求
文章浏览阅读7.6k次,点赞2次,收藏6次。@Service标注的bean,类名:ABDemoService查看源码后发现,原来是经过一个特殊处理:当类的名字是以两个或以上的大写字母开头的话,bean的名字会与类名保持一致public class AnnotationBeanNameGenerator implements BeanNameGenerator { private static final String C..._@service beanname
文章浏览阅读6.9w次,点赞73次,收藏463次。1.前序创建#include<stdio.h>#include<string.h>#include<stdlib.h>#include<malloc.h>#include<iostream>#include<stack>#include<queue>using namespace std;typed_二叉树的建立
文章浏览阅读7.1k次。在Asp.net上使用Excel导出功能,如果文件名出现中文,便会以乱码视之。 解决方法: fileName = HttpUtility.UrlEncode(fileName, System.Text.Encoding.UTF8);_asp.net utf8 导出中文字符乱码
文章浏览阅读2.1k次,点赞4次,收藏23次。第一次实验 词法分析实验报告设计思想词法分析的主要任务是根据文法的词汇表以及对应约定的编码进行一定的识别,找出文件中所有的合法的单词,并给出一定的信息作为最后的结果,用于后续语法分析程序的使用;本实验针对 PL/0 语言 的文法、词汇表编写一个词法分析程序,对于每个单词根据词汇表输出: (单词种类, 单词的值) 二元对。词汇表:种别编码单词符号助记符0beginb..._对pl/0作以下修改扩充。增加单词
文章浏览阅读773次。我在使用adb.exe时遇到了麻烦.我想使用与bash相同的adb.exe shell提示符,所以我决定更改默认的bash二进制文件(当然二进制文件是交叉编译的,一切都很完美)更改bash二进制文件遵循以下顺序> adb remount> adb push bash / system / bin /> adb shell> cd / system / bin> chm..._adb shell mv 权限
文章浏览阅读6.8k次,点赞12次,收藏125次。1. 单目相机标定引言相机标定已经研究多年,标定的算法可以分为基于摄影测量的标定和自标定。其中,应用最为广泛的还是张正友标定法。这是一种简单灵活、高鲁棒性、低成本的相机标定算法。仅需要一台相机和一块平面标定板构建相机标定系统,在标定过程中,相机拍摄多个角度下(至少两个角度,推荐10~20个角度)的标定板图像(相机和标定板都可以移动),即可对相机的内外参数进行标定。下面介绍张氏标定法(以下也这么称呼)的原理。原理相机模型和单应矩阵相机标定,就是对相机的内外参数进行计算的过程,从而得到物体到图像的投影_相机-投影仪标定
文章浏览阅读2.2k次。文章目录Wayland 架构Wayland 渲染Wayland的 硬件支持简 述: 翻译一篇关于和 wayland 有关的技术文章, 其英文标题为Wayland Architecture .Wayland 架构若是想要更好的理解 Wayland 架构及其与 X (X11 or X Window System) 结构;一种很好的方法是将事件从输入设备就开始跟踪, 查看期间所有的屏幕上出现的变化。这就是我们现在对 X 的理解。 内核是从一个输入设备中获取一个事件,并通过 evdev 输入_wayland