用AI生成猫的图片,撸猫人士必备

简介:

我们身边总是不乏各种各样的撸猫人士,面对朋友圈一波又一波晒猫的浪潮,作为学生狗和工作狗的我们只有羡慕的份,更流传有“吸猫穷三代,撸猫毁一生?”的名言,今天营长就为广大爱猫人士发放一份福利,看看如何用AI来生成猫的图片?


用DCGAN生成的猫图片示例



领军研究员 Yann Lecun 称生成式对抗网络( Generative Adverserial Networks, GAN )是“过去20年里机器学习中最棒的想法”。因为这种网络结构的出现,我们才能在今天搭建一个可以生成栩栩如生的猫图片的 AI 系统。这是不是很令人振奋?

DCGAN的训练过程


完整代码(Github):

https://gist.github.com/simoninithomas/c7d1e80810ef838330d7dab068d6b26f#file-training-py


如果你使用过 Python、Tensorflow,学习过深度学习、CNNs(卷积神经网络),将对理解代码大有裨益。


什么是 DCGAN?


深度卷积生成对抗网络(Deep Convolutional Generative Adverserial Networks,DCGAN)是一种深度学习架构,它会生成和训练集中数据相似的结果。


这一模型用卷积层代替了生成对抗网络(GAN)模型中的全连接层。


为了解释 DCGAN 是如何运行的,我们用艺术专家和冒牌专家来做比喻。

冒牌专家( 即“生成器” )企图模仿梵高的画作生成图片并把它当做真实的梵高作品。



而另一边,艺术专家( 即“分类器” )试图利用它们对梵高画作的了解来识别出赝品( 即生成图片 )。



随着时间推移,艺术专家鉴别赝品的技术不断长进,冒牌专家仿作的能力也不断提高。



如我们所见,DCGANs 由两个互相对抗的深度神经网络组成。


  • 生成器是一个仿造者,生成和真实数据相似的结果。它本身不知道真实数据是什么样,但会从另一个模型的反馈信息中学习和调整。

  • 分类器是一个检测者,通过与真实数据比较来确定伪造数据(即模型生成的图片),但尽力不对真实数据报错。这一部分会为生成器的反向传播服务。


DCGAN工作流程示例


  • 生成器会加入随机噪声向量,生成图片;

  • 这张图片被输入给分类器,和训练集进行比较;

  • 最后分类器返回一个 0(伪造图像)和 1(真实图像)之间的数字。


    让我们来创建 DCGAN 吧!


    现在,我们可以准备创建AI了。


    在这部分,我们将关注模型的主要元素。若你想看所有代码,请点这里的 notebook(https://github.com/simoninithomas/CatDCGAN/blob/master/Cat%20DCGAN.ipynb)。


    输入部分


    先创建输入占位符:分类器:inputs_real,生成器:inputs_z。


    注意,我们用两个学习率,一个是生成器的学习率,一个是分类器的学习率。


    DCGANs 对超参数特别敏感,所以精确调参尤其重要。


    def model_inputs(real_dim, z_dim):
       """    Create the model inputs
       :param real_dim: tuple containing width, height and channels
       :param z_dim: The dimension of Z
       :return: Tuple of (tensor of real input images, tensor of z data, learning rate G, learning rate D)
       """
       # inputs_real for Discriminator
       inputs_real = tf.placeholder(tf.float32, (None, *real_dim), name='inputs_real')
     
       # inputs_z for Generator
       inputs_z = tf.placeholder(tf.float32, (None, z_dim), name="input_z")
       
       # Two different learning rate : one for the generator, one for the discriminator
       learning_rate_G = tf.placeholder(tf.float32, name="learning_rate_G")
       
       learning_rate_D = tf.placeholder(tf.float32, name="learning_rate_D")
       
       return inputs_real, inputs_z, learning_rate_G, learning_rate_D


    分类器和生成器


    我们用函数 tf.variable_scope 的原因有两个:


    • 第一,我们想要保证所有变量名称都以 generator 或 discriminator 开头,这将为我们之后训练两个网络提供帮助。

    • 第二,我们要用不同的输入重复训练网络:对于生成器,既要训练它,也要在训练后从生成图像中采样;对于分类器,我们需要在生成图像和真实图像间共用变量。



    我们先来创建分类器。记住,要用真实或生成图像作为输入,然后输出分数。


    需要注意的技术点:


    • 关键点是在每个卷积层加倍过滤器的尺寸;

    • 不建议进行下采样,我们只用一定步长的卷积层;

    • 每层都使用 batch 标准化(输入层除外),因为它会减小协方差转变。想了解更多信息的话请看这篇文章(https://medium.com/@ageitgey/machine-learning-is-fun-80ea3ec3c471)。

    • 我们用 Leaky ReLU 作为激活函数,因为它能帮助避免梯度消失问题。


    def discriminator(x, is_reuse=False, alpha = 0.2):
       ''' Build the discriminator network.
           Arguments
           ---------
           x : Input tensor for the discriminator
           n_units: Number of units in hidden layer
           reuse : Reuse the variables with tf.variable_scope
           alpha : leak parameter for leaky ReLU
           Returns
           -------
           out, logits:
       '''

       with tf.variable_scope("discriminator", reuse = is_reuse):
           # Input layer 128*128*3 --> 64x64x64
           # Conv --> BatchNorm --> LeakyReLU  
           conv1 = tf.layers.conv2d(inputs = x,
                                   filters = 64,
                                   kernel_size = [5,5],
                                   strides = [2,2],
                                   padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name='conv1')
           batch_norm1 = tf.layers.batch_normalization(conv1,
                                                      training = True,
                                                      epsilon = 1e-5,
                                                        name = 'batch_norm1')
           conv1_out = tf.nn.leaky_relu(batch_norm1, alpha=alpha, name="conv1_out")
           # 64x64x64--> 32x32x128
           # Conv --> BatchNorm --> LeakyReLU  
           conv2 = tf.layers.conv2d(inputs = conv1_out,
                                   filters = 128,
                                   kernel_size = [5, 5],
                                   strides = [2, 2],
                                   padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name='conv2')
           batch_norm2 = tf.layers.batch_normalization(conv2,
                                                      training = True,
                                                      epsilon = 1e-5,
                                                        name = 'batch_norm2')
           conv2_out = tf.nn.leaky_relu(batch_norm2, alpha=alpha, name="conv2_out")
           # 32x32x128 --> 16x16x256
           # Conv --> BatchNorm --> LeakyReLU  
           conv3 = tf.layers.conv2d(inputs = conv2_out,
                                   filters = 256,
                                   kernel_size = [5, 5],
                                   strides = [2, 2],
                                   padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name='conv3')
           batch_norm3 = tf.layers.batch_normalization(conv3,
                                                      training = True,
                                                      epsilon = 1e-5,
                                                   name = 'batch_norm3')
           conv3_out = tf.nn.leaky_relu(batch_norm3, alpha=alpha, name="conv3_out")
           # 16x16x256 --> 16x16x512
           # Conv --> BatchNorm --> LeakyReLU  
           conv4 = tf.layers.conv2d(inputs = conv3_out,
                                   filters = 512,
                                   kernel_size = [5, 5],
                                   strides = [1, 1],
                                   padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name='conv4')
           batch_norm4 = tf.layers.batch_normalization(conv4,
                                                      training = True,
                                                      epsilon = 1e-5,
                                                   name = 'batch_norm4')
           conv4_out = tf.nn.leaky_relu(batch_norm4, alpha=alpha, name="conv4_out")
           # 16x16x512 --> 8x8x1024
           # Conv --> BatchNorm --> LeakyReLU  
           conv5 = tf.layers.conv2d(inputs = conv4_out,
                                   filters = 1024,
                                   kernel_size = [5, 5],
                                   strides = [2, 2],
                                   padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name='conv5')
           batch_norm5 = tf.layers.batch_normalization(conv5,
                                                      training = True,
                                                      epsilon = 1e-5,
                                                   name = 'batch_norm5')
           conv5_out = tf.nn.leaky_relu(batch_norm5, alpha=alpha, name="conv5_out")
           # Flatten it
           flatten = tf.reshape(conv5_out, (-1, 8*8*1024))
           # Logits
           logits = tf.layers.dense(inputs = flatten,
                                   units = 1,
                                   activation = None)
           out = tf.sigmoid(logits)
           return out, logits


    再来创建生成器。记住,用随机噪声向量(z)作为输入,根据转置的卷积层输出生成图像。


    其主要思想是在每层将过滤器尺寸减半,而将图片尺寸加倍。研究已经发现,用 tanh 作为输出层的激活函数时,生成器的表现最好。


    def generator(z, output_channel_dim, is_train=True):
       ''' Build the generator network.
           Arguments
           ---------
           z : Input tensor for the generator
           output_channel_dim : Shape of the generator output
           n_units : Number of units in hidden layer
           reuse : Reuse the variables with tf.variable_scope
           alpha : leak parameter for leaky ReLU
           Returns
           -------
           out:
       '''

       with tf.variable_scope("generator", reuse= not is_train):
           # First FC layer --> 8x8x1024
           fc1 = tf.layers.dense(z, 8*8*1024)
           # Reshape it
           fc1 = tf.reshape(fc1, (-1, 8, 8, 1024))
           # Leaky ReLU
           fc1 = tf.nn.leaky_relu(fc1, alpha=alpha)
           # Transposed conv 1 --> BatchNorm --> LeakyReLU
           # 8x8x1024 --> 16x16x512
           trans_conv1 = tf.layers.conv2d_transpose(inputs = fc1,
                                     filters = 512,
                                     kernel_size = [5,5],
                                     strides = [2,2],
                                     padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name="trans_conv1")
           # Transposed conv 1 --> BatchNorm --> LeakyReLU
           # 8x8x1024 --> 16x16x512
           trans_conv1 = tf.layers.conv2d_transpose(inputs = fc1,
                                     filters = 512,
                                     kernel_size = [5,5],
                                     strides = [2,2],
                                     padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name="trans_conv1")
           batch_trans_conv1 = tf.layers.batch_normalization(inputs = trans_conv1, training=is_train, epsilon=1e-5, name="batch_trans_conv1")
           trans_conv1_out = tf.nn.leaky_relu(batch_trans_conv1, alpha=alpha, name="trans_conv1_out")
           # Transposed conv 2 --> BatchNorm --> LeakyReLU
           # 16x16x512 --> 32x32x256
           trans_conv2 = tf.layers.conv2d_transpose(inputs = trans_conv1_out,
                                     filters = 256,
                                     kernel_size = [5,5],
                                     strides = [2,2],
                                     padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name="trans_conv2")
           batch_trans_conv2 = tf.layers.batch_normalization(inputs = trans_conv2, training=is_train, epsilon=1e-5, name="batch_trans_conv2")
           trans_conv2_out = tf.nn.leaky_relu(batch_trans_conv2, alpha=alpha, name="trans_conv2_out")
           # Transposed conv 3 --> BatchNorm --> LeakyReLU
           # 32x32x256 --> 64x64x128
           trans_conv3 = tf.layers.conv2d_transpose(inputs = trans_conv2_out,
                                     filters = 128,
                                     kernel_size = [5,5],
                                     strides = [2,2],
                                     padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name="trans_conv3")
           batch_trans_conv3 = tf.layers.batch_normalization(inputs = trans_conv3, training=is_train, epsilon=1e-5, name="batch_trans_conv3")
           trans_conv3_out = tf.nn.leaky_relu(batch_trans_conv3, alpha=alpha, name="trans_conv3_out")
           # Transposed conv 4 --> BatchNorm --> LeakyReLU
           # 64x64x128 --> 128x128x64
           trans_conv4 = tf.layers.conv2d_transpose(inputs = trans_conv3_out,
                                     filters = 64,
                                     kernel_size = [5,5],
                                     strides = [2,2],
                                     padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name="trans_conv4")
           batch_trans_conv4 = tf.layers.batch_normalization(inputs = trans_conv4, training=is_train, epsilon=1e-5, name="batch_trans_conv4")
           trans_conv4_out = tf.nn.leaky_relu(batch_trans_conv4, alpha=alpha, name="trans_conv4_out")
           # Transposed conv 5 --> tanh
           # 128x128x64 --> 128x128x3
           logits = tf.layers.conv2d_transpose(inputs = trans_conv4_out,
                                     filters = 3,
                                     kernel_size = [5,5],
                                     strides = [1,1],
                                     padding = "SAME",
                                   kernel_initializer=tf.truncated_normal_initializer(stddev=0.02),
                                   name="logits")
           out = tf.tanh(logits, name="out")
           return out



    分类器和生成器的损失


    因为我们是同时训练分类器和生成器,因此,两个网络的损失都需要计算。

    我们的目标是使分类器认为图片为真实图片时输出“ 1 ”,认为图片是生成图片时输出“ 0 ”。因此,我们需要设计能够反映这一特点的损失函数。


    分类器的损失是真实和生成图片的损失之和:


    d_loss = d_loss_real + d_loss_fake  


    d_loss_real 是分类器将真实图片错误地预测为生成图片时的损失。它的计算如下:


    • 用 d_logits_real ,所有标签均为1(因为所有数据都是真实的);

    • labels = tf.ones_like(tensor) * (1 - smooth) ,使用标签平滑:也就是略微减小标签,例如从 1.0 变为 0.9 ,从而使分类器泛化地更好。

    • d_loss_fake 是分类器预测一张图片为真实图片、但实际是生成图片时的损失。

    • 用 d_logits_fake ,所有标签都为0.


    生成器的损失仍使用分类器中的 d_logits_fake ,但标签均为1,因为生成器要迷惑分类器。


    def model_loss(input_real, input_z, output_channel_dim, alpha):
       """
       Get the loss for the discriminator and generator
       :param input_real: Images from the real dataset
       :param input_z: Z input
       :param out_channel_dim: The number of channels in the output image
       :return: A tuple of (discriminator loss, generator loss)
       """
     
        # Generator network here    g_model = generator(input_z, output_channel_dim)  
       # g_model is the generator output    
       # Discriminator network here    d_model_real, d_logits_real = discriminator(input_real, alpha=alpha)    d_model_fake, d_logits_fake = discriminator(g_model,is_reuse=True, alpha=alpha)    
       # Calculate losses    d_loss_real = tf.reduce_mean(
                     tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_real,                                                          labels=tf.ones_like(d_model_real)))    d_loss_fake = tf.reduce_mean(
                     tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake,                                                          labels=tf.zeros_like(d_model_fake)))    d_loss = d_loss_real + d_loss_fake
       g_loss = tf.reduce_mean(
                tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake,                                                     labels=tf.ones_like(d_model_fake)))    
       return d_loss, g_loss


    优化器


    计算损失后,我们需要分别更新生成器和分类器。


    要更新生成器和分类器,我们需要在每部分用 tf.trainable_variables() 获取变量,这样便创建了一个包含已在图中定义好的所有变量的列表。


    def model_optimizers(d_loss, g_loss, lr_D, lr_G, beta1):
       """
       Get optimization operations
       :param d_loss: Discriminator loss Tensor
       :param g_loss: Generator loss Tensor
       :param learning_rate: Learning Rate Placeholder
       :param beta1: The exponential decay rate for the 1st moment in the optimizer
       :return: A tuple of (discriminator training operation, generator training operation)
       """
       
       # Get the trainable_variables, split into G and D parts    t_vars = tf.trainable_variables()
       g_vars = [var for var in t_vars if var.name.startswith("generator")]
       d_vars = [var for var in t_vars if var.name.startswith("discriminator")]
       update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)
       # Generator update    gen_updates = [op for op in update_ops if op.name.startswith('generator')]
       # Optimizers    with tf.control_dependencies(gen_updates):
           d_train_opt = tf.train.AdamOptimizer(learning_rate=lr_D, beta1=beta1).minimize(d_loss, var_list=d_vars)        g_train_opt = tf.train.AdamOptimizer(learning_rate=lr_G, beta1=beta1).minimize(g_loss, var_list=g_vars)        
           return d_train_opt, g_train_opt


    训练


    现在,我们来执行训练函数。


    想法很简单:


    • 每迭代5次保存一次模型;

    • 每训练10个 batch 的图片就保存一张;

    • 每迭代15次将 g_loss , d_loss 和生成图片可视化一次。这样做的原因很简单:显示太多图片的话,Jupyter Notebook 可能会出错。

    • 或者,我们也可以直接通过加载保存的模型来查看图片(这样会节省20h的训练时间)。


    def train(epoch_count, batch_size, z_dim, learning_rate_D, learning_rate_G, beta1, get_batches, data_shape, data_image_mode, alpha):
       """
       Train the GAN
       :param epoch_count: Number of epochs
       :param batch_size: Batch Size
       :param z_dim: Z dimension
       :param learning_rate: Learning Rate
       :param beta1: The exponential decay rate for the 1st moment in the optimizer
       :param get_batches: Function to get batches
       :param data_shape: Shape of the data
       :param data_image_mode: The image mode to use for images ("RGB" or "L")
       """

       # Create our input placeholders
       input_images, input_z, lr_G, lr_D = model_inputs(data_shape[1:], z_dim)
       # Losses
       d_loss, g_loss = model_loss(input_images, input_z, data_shape[3], alpha)
       # Optimizers
       d_opt, g_opt = model_optimizers(d_loss, g_loss, lr_D, lr_G, beta1)
       i = 0
       version = "firstTrain"
       with tf.Session() as sess:
           sess.run(tf.global_variables_initializer())
           # Saver
           saver = tf.train.Saver()
           num_epoch = 0
           if from_checkpoint == True:
               saver.restore(sess, "./models/model.ckpt")
               show_generator_output(sess, 4, input_z, data_shape[3], data_image_mode, image_path, True, False)
           else:
               for epoch_i in range(epoch_count):        
                   num_epoch += 1
                   if num_epoch % 5 == 0:
                       # Save model every 5 epochs
                       #if not os.path.exists("models/" + version):
                       #    os.makedirs("models/" + version)
                       save_path = saver.save(sess, "./models/model.ckpt")
                       print("Model saved")
                   for batch_images in get_batches(batch_size):
                       # Random noise
                       batch_z = np.random.uniform(-1, 1, size=(batch_size, z_dim))
                       i += 1
                       # Run optimizers
                       _ = sess.run(d_opt, feed_dict={input_images: batch_images, input_z: batch_z, lr_D: learning_rate_D})
                       _ = sess.run(g_opt, feed_dict={input_images: batch_images, input_z: batch_z, lr_G: learning_rate_G})
                       if i % 10 == 0:
                           train_loss_d = d_loss.eval({input_z: batch_z, input_images: batch_images})
                           train_loss_g = g_loss.eval({input_z: batch_z})
                           # Save it
                           image_name = str(i) + ".jpg"
                           image_path = "./images/" + image_name
                           show_generator_output(sess, 4, input_z, data_shape[3], data_image_mode, image_path, True, False)
       return losses, samples


    怎样运行模型


    你不能在自己的笔记本上运行这个模型——除非你有自己的 GPU,或者准备好等个十来年。


    因此,你最好用在线 GPU 服务,如 AWS 或者 FloydHub 。我个人训练这个 DCGAN 模型花了 20 个小时,用的是 Microsoft Azure 和他们的深度学习虚拟机。


    Deep Learning Virtual Machine:

    https://medium.com/@ageitgey/machine-learning-is-fun-80ea3ec3c471


    作者 | Thomas Simonini

    原文链接

    https://medium.freecodecamp.org/how-ai-can-learn-to-generate-pictures-of-cats-ba692cb6eae4




    from:https://mp.weixin.qq.com/s?__biz=MzI0ODcxODk5OA==&mid=2247494154&idx=2&sn=389e62b59ef5bef28de98d33ce877bf9&chksm=e99ed9f3dee950e5d64f43ccf99c8ed7d6f70d72c1b41d33b5c3f81edc9a737bedc821acdbe0&mpshare=1&scene=23&srcid=0329mq8EWNETII3CbnBvcouq%23rd

    目录
    相关文章
    |
    4月前
    |
    人工智能 监控 安全
    人体姿态[站着、摔倒、坐、深蹲、跑]检测数据集(6000张图片已划分、已标注)| AI训练适用于目标检测
    本数据集包含6000张已标注人体姿态图片,覆盖站着、摔倒、坐、深蹲、跑五类动作,按5:1划分训练集与验证集,标注格式兼容YOLO等主流框架,适用于跌倒检测、健身分析、安防监控等AI目标检测任务,开箱即用,助力模型快速训练与部署。
    |
    4月前
    |
    人工智能 监控 算法
    人群计数、行人检测数据集(9000张图片已划分、已标注) | AI训练适用于目标检测任务
    本数据集包含9000张已标注、已划分的行人图像,适用于人群计数与目标检测任务。支持YOLO等主流框架,涵盖街道、商场等多种场景,标注精准,结构清晰,助力AI开发者快速训练高精度模型,应用于智慧安防、人流统计等场景。
    人群计数、行人检测数据集(9000张图片已划分、已标注) | AI训练适用于目标检测任务
    |
    4月前
    |
    机器学习/深度学习 人工智能 算法
    用于实验室智能识别的目标检测数据集(2500张图片已划分、已标注) | AI训练适用于目标检测任务
    本数据集包含2500张已标注实验室设备图片,涵盖空调、灭火器、显示器等10类常见设备,适用于YOLO等目标检测模型训练。数据多样、标注规范,支持智能巡检、设备管理与科研教学,助力AI赋能智慧实验室建设。
    用于实验室智能识别的目标检测数据集(2500张图片已划分、已标注) | AI训练适用于目标检测任务
    |
    4月前
    |
    机器学习/深度学习 人工智能 监控
    面向智慧牧场的牛行为识别数据集(5000张图片已划分、已标注) | AI训练适用于目标检测任务
    本数据集包含5000张已标注牛行为图片,涵盖卧、站立、行走三类,适用于YOLO等目标检测模型训练。数据划分清晰,标注规范,场景多样,助力智慧牧场、健康监测与AI科研。
    面向智慧牧场的牛行为识别数据集(5000张图片已划分、已标注) | AI训练适用于目标检测任务
    |
    6月前
    |
    机器学习/深度学习 人工智能 自动驾驶
    交通标识与信号灯数据集(1000张图片已划分、已标注)| AI训练适用于目标检测任务
    在智能驾驶与智慧交通的研究中,交通标识与信号灯识别 是最基础且最关键的任务之一。为了方便研究人员和开发者快速上手目标检测模型训练,本数据集提供了 1000张交通场景图片,并且已经按照目标检测任务的需求完成了 数据标注与划分。该数据集可直接应用于 YOLO、Faster R-CNN、SSD 等深度学习模型的训练与测试。
    交通标识与信号灯数据集(1000张图片已划分、已标注)| AI训练适用于目标检测任务
    |
    4月前
    |
    人工智能 编解码 搜索推荐
    AI智能换背景,助力电商图片营销升级
    电商产品图换背景是提升销量与品牌形象的关键。传统抠图耗时费力,AI技术则实现一键智能换背景,高效精准。本文详解燕雀光年AI全能设计、Canva、Remove.bg等十大AI工具,涵盖功能特点与选型建议,助力商家快速打造高质量、高吸引力的商品图,提升转化率与品牌价值。(238字)
    494 0
    |
    存储 人工智能 开发工具
    AI助理化繁为简,速取代码参数——使用python SDK 处理OSS存储的图片
    只需要通过向AI助理提问的方式输入您的需求,即可瞬间获得核心流程代码及参数,缩短学习路径、提升开发效率。
    1786 5
    AI助理化繁为简,速取代码参数——使用python SDK 处理OSS存储的图片
    |
    机器学习/深度学习 人工智能 编解码
    AI虫子种类识别数据集(近3000张图片已划分、已标注)|适用于YOLO系列深度学习分类检测任务【数据集分享】
    本数据集包含近3000张已划分、标注的虫子图像,适用于YOLO系列模型的目标检测与分类任务。涵盖7类常见虫子,标注采用YOLO格式,结构清晰,适合农业智能化、小样本学习及边缘部署研究。数据来源多样,标注精准,助力AI虫害识别落地应用。
    |
    9月前
    |
    存储 机器学习/深度学习 人工智能
    多模态RAG实战指南:完整Python代码实现AI同时理解图片、表格和文本
    本文探讨了多模态RAG系统的最优实现方案,通过模态特定处理与后期融合技术,在性能、准确性和复杂度间达成平衡。系统包含文档分割、内容提取、HTML转换、语义分块及向量化存储五大模块,有效保留结构和关系信息。相比传统方法,该方案显著提升了复杂查询的检索精度(+23%),并支持灵活升级。文章还介绍了查询处理机制与优势对比,为构建高效多模态RAG系统提供了实践指导。
    2437 0
    多模态RAG实战指南:完整Python代码实现AI同时理解图片、表格和文本