热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

Tensorflow实战6:Tensorflow实现AlexNet

算法实现过程的详细介绍\quad首先需要介绍一下AlexNet,2012年Hinton的学生AlexKrizhevsky提出了深度卷积神经网络模型AlexNet&#x
算法实现过程的详细介绍

\quad首先需要介绍一下AlexNet,2012年Hinton的学生Alex Krizhevsky提出了深度卷积神经网络模型AlexNet,它可以算是LeNet的一种更宽更深的版本。AlexNet中包含了几个技术点,也首次在CNN中成功应用了ReLU,Dropout和LRN等Trick。同时AlexNet也使用了GPU进行运算加速,作者开源了他们在GPU上训练神经网络的CUDA代码。AlexNet包含了6亿3000万个连接,6000万个参数和65万个神经元,拥有5个卷积层,其中3个卷积层后面连接了连接了最大池化层,最后还有3个全连接层。AlexNet以显著的优势赢得了竞争激烈的ILSVRC 2012的比赛,top-5的错误率降低到了16.4%,相比第二名的成绩26.2%错误率有了巨大的提升。AlexNet可以说是神经网络在低谷期后的第一次发声,确立了深度学习在计算机视觉的统治地位,同时也推动了深度学习在语音识别,自然语言处理,强化学习等领域的拓展。
\quadAlexNet将Lenet的思想发扬光大,把CNN的基本原理应用到了很深很宽的网络中。AlexNet主要用到的新技术如下:

  • 使用ReLU作为CNN的激活函数,并验证其效果在比较深的网络中超过了Sigmoid,成功解决了Sigmoid在网络比较深时的梯度弥散问题。虽然ReLU激活函数在不久之前就提出了,但是直到AlexNet的出现才将其发扬光大。
  • 训练时使用Dropout随机忽略一部分神经元,以避免模型过拟合。Dropout虽然有单独的论文论述,但是AlexNet将其实用化,通过实践证明了它的效果。在AlexNet中主要是最后几个全连接层使用了Dropout。
  • 在CNN中使用重叠的最大池化。之前CNN中普遍使用平均池化,AlexNet全部使用最大池化,避免平均池化的模糊化效果。并且AlexNet中提出让步长比池化核的尺寸小,这样池化层的输出之间有重叠和覆盖,提升了特征的丰富性。
  • 提出了LRN层,对局部神经元的活动创建竞争机制,使得其中响应比较大的变得相对更大,并抑制其他反馈较小的神经元,增强了模型的泛化能力。
  • 使用CUDA加速深度卷积网络的训练,利用GPU强大的并行计算能力,处理神经网络训练时大量的矩阵运算。AlexNet使用了两块GTX 580 GPU进行训练,单个GTX 580只有3G显存,这限制了可训练的网络的最大规模。因此,作者将AlexNet分布在两个GPU上,在每个GPU的显存中存储一半的神经元的参数。因为GPU之间的通信方便,可以互相访问显存,而不需要通过主机内存,所以同时使用多块GPU也是非常高效的。同时,AlexNet的设计让GPU之间的通信只在网络的某些层进行,控制了通信的性能损耗。
  • 数据增强,随机的人从256 ×\times× 256的原始图像截取224 ×\times× 224大小的区域(以及水平翻转的镜像),相当于增加了(256-224)^2$\times$2=2048倍的数据量,使用数据增强之后可以大大减轻过拟合,提升泛化能力。进行预测时,则是取图片的4个角加中间共5个位置,并进行左右翻转,一共获得10张图片,对他们进行预测并对10次结果求均值。同时,AlexNet论文中提到了会对RGB图像数据进行PCA处理,并对主成分做一个标准差为0.1的高斯扰动,增加一些噪声,这个Trick可以让错误率再下降1%

  1. 首先导入会用到的系统库,包括datetime,math和time,并载入tensorflow。

#coding=utf-8
from datetime import datetime
import math
import time
import tensorflow as tf

  1. 这里设置batch_size为32,num_batches为100,即总共测试100个batch的数据。

#设置batch_size=32,num_batches为100
batch_size = 32
num_batches = 100

  1. 定义一个用来显示网络每一层结构的函数print_actications,展示每个卷积层或池化层输出tensor的尺寸。这个函数接受一个tensor作为输入,并显示其名称(t.op.name)和tensor尺寸(t.get_shape().as_list())。

def print_activations(t):print(t.op.name, ' ', t.get_shape().as_list())

  1. 接下来设计AlexNet网络的结构。我们先定义函数inference,它接受images作为输入,返回最后一层pool5(第5个池化层)及parameters(AlexNet中所有需要训练的模型参数)。这个inference函数将会很大,包括多个卷积层和池化层,下面会拆分成几个小段讲解。
    \quad首先是第一个卷积层conv1,这里使用Tensorflow中的name_scope,通过with tf.name_scope(‘conv1’) as scope可以将scope内生成的Variabler自动命名为conv1/xxx,便于区分不同卷积层之间的组件。然后定义第一个卷积层,和之前一样使用tf.truncated_normal截断的正态分布函数(标准差为0.1)初始化卷积核的参数kernel。卷积核尺寸为11 ×\times× 11,颜色通道为3,卷积核尺寸为64.准备好了kernel,再使用tf.nn.conv2d对输入images完成卷积操作,我们将strides步长设置为4*4(即在图片上4 ×\times× 4区域只取样一次,横向间隔是4,纵向间隔也是4,每次取样的卷积核大小都为11 ×\times× 11),padding模式设为SAME。将卷积层的biases全部初始化为0,再使用tf.nn.bias_add将conv和bias加起来,并使用激活函数tf.nn.relu对结果进行非线性处理。最后使用print_activations将这一层最后输出的tensor_conv1的结构打印出来,再将这一层可训练的参数kernel,biases添加到parameters。

def inference(images):parameters = []with tf.name_scope('conv1') as scope:#第一个卷积层kernel = tf.Variable(tf.truncated_normal([11, 11, 3, 64], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(images, kernel, [1, 4, 4, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32), trainable=True, name='bias')bias = tf.nn.bias_add(conv, biases)conv1 = tf.nn.relu(bias, name=scope)print_activations(conv1)parameters += [kernel, biases]

\quad 在第一个卷积层后面再添加LRN层和最大池化层。先使用tf.nn.lrn对前面输出的tensor conv1进行LRN处理,这里使用的depth_radius设为4,bias设为1,alpha为0.001/9,beta为0.75,基本都是AlexNet的论文中的推荐值,不过目前除了AlexNet,其他经典的卷积神经网络模型基本都放弃了LRN(主要是效果不明显),而我们使用LRN也会让前馈,反馈的速度大大下降(整体速度下降到1/3),读者可以自主选择是否使用LRN。下面使用tf.nn.max_pool对前面的输出lrn1进行最大池化处理,这里的池化尺寸为3 ×\times× 3,即将3 ×\times× 3大小的像素块降为1 ×\times× 1的像素,取样的步长为2 ×\times× 2,padding模式为VALID,即取样时不能超过边框,不像SAME模式那样可以填充边界外的点。最后将输出结果pool1的结构打印出来。

#添加LRN和最大池化层lrn1 = tf.nn.lrn(conv1, 4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn1')pool1 = tf.nn.max_pool(lrn1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool1')print_activations(pool1)

\quad接下来设计第2个卷积层,卷积核的尺寸为5 ×\times× 5,输入通道数(即上一层的输出通道数,也就是上一层的卷积核数量)为64,卷积核数量为192。同时,卷积的步长也全部设为1,即扫描全图像素。

#第二个卷积层with tf.name_scope('conv2') as scope:kernel = tf.Variable(tf.truncated_normal([5, 5, 64, 192], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(pool1, kernel, [1, 1, 1, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[192], dtype=tf.float32), trainable=True, name='biases')bias = tf.nn.bias_add(conv, biases)conv2 = tf.nn.relu(bias, name=scope)parameters += [kernel, biases]print_activations(conv2)

\quad接下来对第2个卷积层的输出conv2进行处理,同样是先做LRN处理,再进行最大池化处理,参数和之前完全一样

#对conv2处理lrn2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn2')pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool2')print_activations(pool2)

\quad下面创建第3个卷积层,基本结构和前面两个类似,也只是参数不同。这一层的卷积核尺寸为3 ×\times× 3,输入的通道数为192,卷积核数量继续扩大为384,同时卷积的步长全为1,其他地方和前面保持一致。

#第3个卷积层with tf.name_scope('conv3') as scope:kernel = tf.Variable(tf.truncated_normal([3, 3, 192, 384], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(pool2, kernel, [1, 1, 1, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[384], dtype=tf.float32), trainable=True, name='biases')bias = tf.nn.bias_add(conv, biases)conv3 = tf.nn.relu(bias, name=scope)parameters += [kernel, biases]print_activations(conv3)

\quad第4个卷积层和之前也类似,这一层的卷积核尺寸为3 ×\times× 3,输入通道数384,但是卷积核尺寸数量将为256。

#第4个卷积层with tf.name_scope('conv4') as scope:kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 256], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(conv3, kernel, [1, 1, 1, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32), trainable=True, name='biases')bias = tf.nn.bias_add(conv, biases)conv4 = tf.nn.relu(bias, name=scope)parameters += [kernel, biases]print_activations(conv4)

\quad最后的第5个卷积层同样是3 ×\times× 3大小的卷积核,输入通道数为256,卷积核数量也为256。

#第5个卷积层with tf.name_scope('conv5') as scope:kernel = tf.Variable(tf.truncated_normal([3, 3, 256, 256], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(conv4, kernel, [1, 1, 1, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32), trainable=True, name='biases')bias = tf.nn.bias_add(conv, biases)conv5 = tf.nn.relu(bias, name=scope)parameters += [kernel, biases]print_activations(conv5)

\quad在第5个卷积层之后,还有一个最大池化层,这个池化层和前两个卷积层一致,最后我们返回这个池化层的输出pool5。至此,inference函数就完成了,它可以创建AlexNet的卷积部分。在正式使用AlexNet来训练或预测时,还需要添加3个全连接层,隐含节点分别为4096,4096和1000。由于最后3个全连接层的计算量很小,所以没放到速度测评中,他们对耗时的影响很小。

#最大池化层pool5pool5 = tf.nn.max_pool(conv5, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool5')print_activations(pool5)#未添加全连接层,因为对计算耗时影响小return pool5, parameters

  1. 开始实现一个评估AlexNet每轮计算时间的函数time_tensorflow_rn。这个函数的第一个输入是Tensorflow的Session,第二个变量是需要评测的运算因子,第三个变量是测试的名称。先定义预热轮数num_steps_burn_in=10,它的作用是给程序热身,头几轮迭代有显存加载,cache的命中等问题因此可以跳动,我们只需要考量10轮迭代之后的计算时间。同时,也记录总时间total_duratiuon和平方和total_duration_squared用以计算方差。我们进行num_batchs+num_steps_burn_in次迭代计算,使用time.time()记录时间,每次迭代通过session.run(target)执行。在初始热身的num_steps_burn_in次迭代后,每10轮迭代显示当前迭代需要的时间。同时每轮将total_duration和total_duration_squared累加,以便后面计算每轮耗时的均值和标准差。在循环结束后,计算每轮迭代的平均耗时nm和标准差sd,最后将结果显示出来。这样就完成了计算每轮迭代耗时的评测函数。

#定义AlexNet的每轮时间评估函数
def time_tensorflow_run(session, target, info_string):num_steps_burn_in = 10 #程序预热total_durations = 0.0total_duration_squared = 0.0for i in range(num_batches + num_steps_burn_in):start_time = time.time()_ = session.run(target)duration = time.time() - start_timeif i >= num_steps_burn_in:if not i % 10:print('%s: step %d, duration = %.3f'%(datetime.now(), i - num_steps_burn_in, duration))total_durations += durationtotal_duration_squared += duration * duration#计算每轮迭代的平均耗时和标准差sd,最后将结果显示出来mn = total_durations / num_batchesvr = total_duration_squared / num_batches - mn * mnsd = math.sqrt(vr)print('%s: %s across %d steps, %.3f +/- %.3f sec / batch'%(datetime.now(), info_string, num_batches, mn, sd))

  1. 接下来是主函数run_benchmark。首先使用with tf.Graph().as_default()定义默认的Graph方便后面使用。如前面所说,我们并不使用ImageNet数据集来训练,只使用随机图片数据测试前馈和反馈计算的耗时。我们使用tf.random_normal函数构造正态分布(标准差为0.1)的随机tensor,第一个维度是batch_size,即每轮迭代的样本数,第二个和第三个维度是图片的尺寸image_size=224,第4个维度是图片的颜色通道数。接下来,使用前面定义的inference函数构建整个AlexNet网络,得到最后一个池化层的输出pool5和网络中需要训练的参数的集合parameters。接下来,我们使用tf.Session()创建新的Session并通过tf.global_variables_initializer()初始化所有参数。再进行AlexNet的foward计算的评测,这里直接使用time_tensorflow_run统计运算时间,传入的target就是pool5,即卷积网络的最后一个池化层的输出。然后进行backward即训练过程的评测,这里和forward有些不同,我们需要给最后的输出pool5设置一个优化目标Loss,我们使用tf.nn.l2_loss计算pool5的loss,再使用tf.gradients求相对于loss的所有模型参数的梯度,这样就模拟了一个训练的过程。当然,训练时还有一个根据梯度更新参数的过程,不过这个计算量很小,就不统计在评测程序里了。最后我们使用time_tensorflow_run统计Backward的运算时间,这里的target就是求整个网络梯度grad的操作。

#定义主函数run_benchmark
def run_benchmark():with tf.Graph().as_default():image_size = 224images = tf.Variable(tf.random_normal([batch_size, image_size, image_size, 3], dtype=tf.float32, stddev=1e-1))pool5, parameters = inference(images)init = tf.global_variables_initializer()sess = tf.Session()sess.run(init)time_tensorflow_run(sess, pool5, "Foward")objective = tf.nn.l2_loss(pool5)grad = tf.gradients(objective, parameters)time_tensorflow_run(sess, grad, "Foward-backward")run_benchmark()

算法实现的完整代码

#coding=utf-8
from datetime import datetime
import math
import time
import tensorflow as tf
#设置batch_size=32,num_batches为100
batch_size = 32
num_batches = 100
#定义一个现实网络每一层结构的函数print_actications,展示每一个卷积层或池化层输出tensor的尺寸。
def print_activations(t):print(t.op.name, ' ', t.get_shape().as_list())
#设计Alexnet的网络结构
def inference(images):parameters = []with tf.name_scope('conv1') as scope:#第一个卷积层kernel = tf.Variable(tf.truncated_normal([11, 11, 3, 64], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(images, kernel, [1, 4, 4, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[64], dtype=tf.float32), trainable=True, name='bias')bias = tf.nn.bias_add(conv, biases)conv1 = tf.nn.relu(bias, name=scope)print_activations(conv1)parameters += [kernel, biases]#添加LRN和最大池化层lrn1 = tf.nn.lrn(conv1, 4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn1')pool1 = tf.nn.max_pool(lrn1, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool1')print_activations(pool1)#第二个卷积层with tf.name_scope('conv2') as scope:kernel = tf.Variable(tf.truncated_normal([5, 5, 64, 192], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(pool1, kernel, [1, 1, 1, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[192], dtype=tf.float32), trainable=True, name='biases')bias = tf.nn.bias_add(conv, biases)conv2 = tf.nn.relu(bias, name=scope)parameters += [kernel, biases]print_activations(conv2)#对conv2处理lrn2 = tf.nn.lrn(conv2, 4, bias=1.0, alpha=0.001/9, beta=0.75, name='lrn2')pool2 = tf.nn.max_pool(lrn2, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool2')print_activations(pool2)#第3个卷积层with tf.name_scope('conv3') as scope:kernel = tf.Variable(tf.truncated_normal([3, 3, 192, 384], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(pool2, kernel, [1, 1, 1, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[384], dtype=tf.float32), trainable=True, name='biases')bias = tf.nn.bias_add(conv, biases)conv3 = tf.nn.relu(bias, name=scope)parameters += [kernel, biases]print_activations(conv3)#第4个卷积层with tf.name_scope('conv4') as scope:kernel = tf.Variable(tf.truncated_normal([3, 3, 384, 256], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(conv3, kernel, [1, 1, 1, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32), trainable=True, name='biases')bias = tf.nn.bias_add(conv, biases)conv4 = tf.nn.relu(bias, name=scope)parameters += [kernel, biases]print_activations(conv4)#第5个卷积层with tf.name_scope('conv5') as scope:kernel = tf.Variable(tf.truncated_normal([3, 3, 256, 256], dtype=tf.float32, stddev=1e-1), name='weights')conv = tf.nn.conv2d(conv4, kernel, [1, 1, 1, 1], padding='SAME')biases = tf.Variable(tf.constant(0.0, shape=[256], dtype=tf.float32), trainable=True, name='biases')bias = tf.nn.bias_add(conv, biases)conv5 = tf.nn.relu(bias, name=scope)parameters += [kernel, biases]print_activations(conv5)#最大池化层pool5pool5 = tf.nn.max_pool(conv5, ksize=[1, 3, 3, 1], strides=[1, 2, 2, 1], padding='VALID', name='pool5')print_activations(pool5)#未添加全连接层,因为对计算耗时影响小return pool5, parameters#定义AlexNet的每轮时间评估函数
def time_tensorflow_run(session, target, info_string):num_steps_burn_in = 10 #程序预热total_durations = 0.0total_duration_squared = 0.0for i in range(num_batches + num_steps_burn_in):start_time = time.time()_ = session.run(target)duration = time.time() - start_timeif i >= num_steps_burn_in:if not i % 10:print('%s: step %d, duration = %.3f'%(datetime.now(), i - num_steps_burn_in, duration))total_durations += durationtotal_duration_squared += duration * duration#计算每轮迭代的平均耗时和标准差sd,最后将结果显示出来mn = total_durations / num_batchesvr = total_duration_squared / num_batches - mn * mnsd = math.sqrt(vr)print('%s: %s across %d steps, %.3f +/- %.3f sec / batch'%(datetime.now(), info_string, num_batches, mn, sd))
#定义主函数run_benchmark
def run_benchmark():with tf.Graph().as_default():image_size = 224images = tf.Variable(tf.random_normal([batch_size, image_size, image_size, 3], dtype=tf.float32, stddev=1e-1))pool5, parameters = inference(images)init = tf.global_variables_initializer()sess = tf.Session()sess.run(init)time_tensorflow_run(sess, pool5, "Foward")objective = tf.nn.l2_loss(pool5)grad = tf.gradients(objective, parameters)time_tensorflow_run(sess, grad, "Foward-backward")run_benchmark()


推荐阅读
  • 通过使用CIFAR-10数据集,本文详细介绍了如何快速掌握Mixup数据增强技术,并展示了该方法在图像分类任务中的显著效果。实验结果表明,Mixup能够有效提高模型的泛化能力和分类精度,为图像识别领域的研究提供了有价值的参考。 ... [详细]
  • 利用 Spring BeanUtils 实现 JavaBean 的深度克隆与属性复制 ... [详细]
  • 深入解析经典卷积神经网络及其实现代码
    深入解析经典卷积神经网络及其实现代码 ... [详细]
  • 在稀疏直接法视觉里程计中,通过优化特征点并采用基于光度误差最小化的灰度图像线性插值技术,提高了定位精度。该方法通过对空间点的非齐次和齐次表示进行处理,利用RGB-D传感器获取的3D坐标信息,在两帧图像之间实现精确匹配,有效减少了光度误差,提升了系统的鲁棒性和稳定性。 ... [详细]
  • 本文深入探讨了数据库性能优化与管理策略,通过实例分析和理论研究,详细阐述了如何有效提升数据库系统的响应速度和处理能力。文章首先介绍了数据库性能优化的基本原则和常用技术,包括索引优化、查询优化和存储管理等。接着,结合实际应用场景,讨论了如何利用容器化技术(如Docker)来部署和管理数据库,以提高系统的可扩展性和稳定性。最后,文章还提供了具体的配置示例和最佳实践,帮助读者在实际工作中更好地应用这些策略。 ... [详细]
  • 在该项目中,参与者需结合历史使用模式和天气数据,以预测华盛顿特区自行车共享系统的租赁需求。数据分析部分首先涉及数据的收集,包括用户骑行记录和气象信息,为后续模型构建提供基础。通过深入的数据预处理和特征工程,确保数据质量和模型准确性,最终实现对自行车租赁需求的有效预测。 ... [详细]
  • 视觉图像的生成机制与英文术语解析
    近期,Google Brain、牛津大学和清华大学等多家研究机构相继发布了关于多层感知机(MLP)在视觉图像分类中的应用成果。这些研究深入探讨了MLP在视觉任务中的工作机制,并解析了相关技术术语,为理解视觉图像生成提供了新的视角和方法。 ... [详细]
  • 如何将TS文件转换为M3U8直播流:HLS与M3U8格式详解
    在视频传输领域,MP4虽然常见,但在直播场景中直接使用MP4格式存在诸多问题。例如,MP4文件的头部信息(如ftyp、moov)较大,导致初始加载时间较长,影响用户体验。相比之下,HLS(HTTP Live Streaming)协议及其M3U8格式更具优势。HLS通过将视频切分成多个小片段,并生成一个M3U8播放列表文件,实现低延迟和高稳定性。本文详细介绍了如何将TS文件转换为M3U8直播流,包括技术原理和具体操作步骤,帮助读者更好地理解和应用这一技术。 ... [详细]
  • 使用Maven JAR插件将单个或多个文件及其依赖项合并为一个可引用的JAR包
    本文介绍了如何利用Maven中的maven-assembly-plugin插件将单个或多个Java文件及其依赖项打包成一个可引用的JAR文件。首先,需要创建一个新的Maven项目,并将待打包的Java文件复制到该项目中。通过配置maven-assembly-plugin,可以实现将所有文件及其依赖项合并为一个独立的JAR包,方便在其他项目中引用和使用。此外,该方法还支持自定义装配描述符,以满足不同场景下的需求。 ... [详细]
  • 本指南介绍了 `requests` 库的基本使用方法,详细解释了其七个主要函数。其中,`requests.request()` 是构建请求的基础方法,支持其他高级功能的实现。此外,我们还重点介绍了如何使用 `requests.get()` 方法来获取 HTML 网页内容,这是进行网页数据抓取和解析的重要步骤。通过这些基础方法,读者可以轻松上手并掌握网页数据抓取的核心技巧。 ... [详细]
  • 经过两天的努力,终于成功解决了半平面交模板题POJ3335的问题。原来是在`OnLeft`函数中漏掉了关键的等于号。通过这次训练,不仅加深了对半平面交算法的理解,还提升了调试和代码实现的能力。未来将继续深入研究计算几何的其他核心问题,进一步巩固和拓展相关知识。 ... [详细]
  • 能够感知你情绪状态的智能机器人即将问世 | 科技前沿观察
    本周科技前沿报道了多项重要进展,包括美国多所高校在机器人技术和自动驾驶领域的最新研究成果,以及硅谷大型企业在智能硬件和深度学习技术上的突破性进展。特别值得一提的是,一款能够感知用户情绪状态的智能机器人即将问世,为未来的人机交互带来了全新的可能性。 ... [详细]
  • Netty框架中运用Protobuf实现高效通信协议
    在Netty框架中,通过引入Protobuf来实现高效的通信协议。为了使用Protobuf,需要先准备好环境,包括下载并安装Protobuf的代码生成器`protoc`以及相应的源码包。具体资源可从官方下载页面获取,确保版本兼容性以充分发挥其性能优势。此外,配置好开发环境后,可以通过定义`.proto`文件来自动生成Java类,从而简化数据序列化和反序列化的操作,提高通信效率。 ... [详细]
  • 利用树莓派畅享落网电台音乐体验
    最近重新拾起了闲置已久的树莓派,这台小巧的开发板已经沉寂了半年多。上个月闲暇时间较多,我决定将其重新启用。恰逢落网电台进行了改版,回忆起之前在树莓派论坛上看到有人用它来播放豆瓣音乐,便萌生了同样的想法。通过一番调试,终于实现了在树莓派上流畅播放落网电台音乐的功能,带来了全新的音乐享受体验。 ... [详细]
  • 本文介绍了一种利用Dom4j库和JFileChooser组件在Java中实现XML文件自定义路径导出的方法。通过创建一个Document对象并设置根元素,结合JFileChooser选择目标路径,实现了灵活的XML文件导出功能。具体步骤包括初始化Document对象、构建XML结构以及使用JFileChooser选择保存路径,确保用户能够方便地将生成的XML文件保存到指定位置。 ... [详细]
author-avatar
手机用户2502854107
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有