Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >利用tensorflow训练简单的生成对抗网络GAN

利用tensorflow训练简单的生成对抗网络GAN

作者头像
狼啸风云
修改于 2022-09-02 12:43:05
修改于 2022-09-02 12:43:05
1.2K00
代码可运行
举报
运行总次数:0
代码可运行

对抗网络是14年Goodfellow Ian在论文Generative Adversarial Nets中提出来的。 原理方面,对抗网络可以简单归纳为一个生成器(generator)和一个判断器(discriminator)之间博弈的过程。整个网络训练的过程中,

两个模块的分工

  • 判断网络,直观来看就是一个简单的神经网络结构,输入就是一副图像,输出就是一个概率值,用于判断真假使用(概率值大于0.5那就是真,小于0.5那就是假)。
  • 生成网络,同样也可以看成是一个神经网络模型,输入是一组随机数Z,输出是一个图像。

两个模块的训练目的

  • 判别网络的目的:就是能判别出来属于的一张图它是来自真实样本集还是假样本集。假如输入的是真样本,网络输出就接近1,输入的是假样本,网络输出接近0,那么很完美,达到了很好判别的目的。
  • 生成网络的目的:生成网络是造样本的,它的目的就是使得自己造样本的能力尽可能强,强到判别网络没法判断是真样本还是假样本。

GAN的训练

  需要注意的是生成模型与对抗模型可以说是完全独立的两个模型,好比就是完全独立的两个神经网络模型,他们之间没有什么联系。

那么训练这样的两个模型的大方法就是:单独交替迭代训练。因为是2个网络,不好一起训练,所以才去交替迭代训练,我们一一来看。 

  首先我们先随机产生一个生成网络模型(当然可能不是最好的生成网络),那么给一堆随机数组,就会得到一堆假的样本集(因为不是最终的生成模型,那么现在生成网络可能就处于劣势,导致生成的样本很糟糕,可能很容易就被判别网络判别出来了说这货是假冒的),但是先不管这个,假设我们现在有了这样的假样本集,真样本集一直都有,现在我们人为的定义真假样本集的标签,因为我们希望真样本集的输出尽可能为1,假样本集为0,很明显这里我们就已经默认真样本集所有的类标签都为1,而假样本集的所有类标签都为0.

  对于生成网络,回想下我们的目标,是生成尽可能逼真的样本。那么原始的生成网络生成的样本你怎么知道它真不真呢?就是送到判别网络中,所以在训练生成网络的时候,我们需要联合判别网络一起才能达到训练的目的。就是如果我们单单只用生成网络,那么想想我们怎么去训练?误差来源在哪里?细想一下没有,但是如果我们把刚才的判别网络串接在生成网络的后面,这样我们就知道真假了,也就有了误差了。所以对于生成网络的训练其实是对生成-判别网络串接的训练,就像图中显示的那样。好了那么现在来分析一下样本,原始的噪声数组Z我们有,也就是生成了假样本我们有,此时很关键的一点来了,我们要把这些假样本的标签都设置为1,也就是认为这些假样本在生成网络训练的时候是真样本。这样才能起到迷惑判别器的目的,也才能使得生成的假样本逐渐逼近为正样本。

下面是代码部分,这里,我们利用训练的两个数据集分别是

  • mnist
  • Celeba

来生成手写数字以及人脸

首先是数据集的下载

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
import math
import os
import hashlib
from urllib.request import urlretrieve
import zipfile
import gzip
import shutil

data_dir = './data'

def download_extract(database_name, data_path):
     """
     Download and extract database
     :param database_name: Database name
     """
     DATASET_CELEBA_NAME = 'celeba'
     DATASET_MNIST_NAME = 'mnist'

     if database_name == DATASET_CELEBA_NAME:
         url = 'https://s3-us-west-1.amazonaws.com/udacity-dlnfd/datasets/celeba.zip'
         hash_code = '00d2c5bc6d35e252742224ab0c1e8fcb'
         extract_path = os.path.join(data_path, 'img_align_celeba')
         save_path = os.path.join(data_path, 'celeba.zip')
         extract_fn = _unzip
     elif database_name == DATASET_MNIST_NAME:
         url = 'http://yann.lecun.com/exdb/mnist/train-images-idx3-ubyte.gz'
         hash_code = 'f68b3c2dcbeaaa9fbdd348bbdeb94873'
         extract_path = os.path.join(data_path, 'mnist')
         save_path = os.path.join(data_path, 'train-images-idx3-ubyte.gz')
         extract_fn = _ungzip

     if os.path.exists(extract_path):
         print('Found {} Data'.format(database_name))
         return

     if not os.path.exists(data_path):
         os.makedirs(data_path)

     if not os.path.exists(save_path):
         with DLProgress(unit='B', unit_scale=True, miniters=1, desc='Downloading {}'.format(database_name)) as pbar:
             urlretrieve(
                 url,
                 save_path,
                 pbar.hook)

     assert hashlib.md5(open(save_path, 'rb').read()).hexdigest() == hash_code, \
         '{} file is corrupted.  Remove the file and try again.'.format(save_path)

     os.makedirs(extract_path)
     try:
         extract_fn(save_path, extract_path, database_name, data_path)
     except Exception as err:
         shutil.rmtree(extract_path)  # Remove extraction folder if there is an error
         raise err

     # Remove compressed data
     os.remove(save_path)

# download mnist
download_extract('mnist', data_dir)
# download celeba
download_extract('celeba', data_dir

我们先看看我们的mnist还有celeba数据集是什么样子

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
 # the number of images
  show_n_images =16
  
  %matplotlib inline
  import os
  from glob import glob
  from matplotlib import pyplot
  
  def get_batch(image_files, width, height, mode):
     data_batch = np.array(
         [get_image(sample_file, width, height, mode) for sample_file in image_files]).astype(np.float32)
 
     # Make sure the images are in 4 dimensions
     if len(data_batch.shape) < 4:
         data_batch = data_batch.reshape(data_batch.shape + (1,))
 
     return data_batch
 
 def images_square_grid(images, mode):
     # Get maximum size for square grid of images
     save_size = math.floor(np.sqrt(images.shape[0]))
 
     # Scale to 0-255
     images = (((images - images.min()) * 255) / (images.max() - images.min())).astype(np.uint8)
 
     # Put images in a square arrangement
     images_in_square = np.reshape(
             images[:save_size*save_size],
             (save_size, save_size, images.shape[1], images.shape[2], images.shape[3]))
     if mode == 'L':
         images_in_square = np.squeeze(images_in_square, 4)
 
     # Combine images to grid image
     new_im = Image.new(mode, (images.shape[1] * save_size, images.shape[2] * save_size))
     for col_i, col_images in enumerate(images_in_square):
         for image_i, image in enumerate(col_images):
             im = Image.fromarray(image, mode)
             new_im.paste(im, (col_i * images.shape[1], image_i * images.shape[2]))
 
     return new_im
 
 mnist_images = get_batch(glob(os.path.join(data_dir, 'mnist/*.jpg'))[:show_n_images], 28, 28, 'L')
 pyplot.imshow(images_square_grid(mnist_images, 'L'), cmap='gray')

mninst:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
show_n_images = 9
 
mnist_images = get_batch(glob(os.path.join(data_dir, 'img_align_celeba/*.jpg'))[:show_n_images], 28, 28, 'RGB')
pyplot.imshow(images_square_grid(mnist_images, 'RGB'))

celeba

现在我们开始搭建网络

这里我建议用GPU来训练,tensorflow的版本最好是1.1.0

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
from distutils.version import LooseVersion
import warnings
import tensorflow as tf

# Check TensorFlow Version
assert LooseVersion(tf.__version__) >= LooseVersion('1.0'), 'Please use TensorFlow version 1.0 or newer.  You are using {}'.format(tf.__version__)
print('TensorFlow Version: {}'.format(tf.__version__))

# Check for a GPU
if not tf.test.gpu_device_name():
    warnings.warn('No GPU found. Please use a GPU to train your neural network.')
else:
    print('Default GPU Device: {}'.format(tf.test.gpu_device_name()))

接着我们要做的是构建输入

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def model_inputs(image_width, image_height, image_channels, z_dim):
    ## Real imag
    inputs_real = tf.placeholder(tf.float32,(None, image_width,image_height,image_channels), name = 'input_real')

    ## input z

    inputs_z = tf.placeholder(tf.float32,(None, z_dim), name='input_z')

    ## Learning rate
    learning_rate = tf.placeholder(tf.float32, name = 'lr')

    return inputs_real, inputs_z, learning_rate

构建Discriminator

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def discriminator(images, reuse=False):
    """
    Create the discriminator network
    :param images: Tensor of input image(s)
    :param reuse: Boolean if the weights should be reused
    :return: Tuple of (tensor output of the discriminator, tensor logits of the discriminator)
    """
    # TODO: Implement Function

    ## scope here

    with tf.variable_scope('discriminator', reuse=reuse):

        alpha = 0.2  ### leak relu coeff

        # drop out probability
        keep_prob = 0.8

        # input layer 28 * 28 * color channel
        x1 = tf.layers.conv2d(images, 128, 5, strides=2, padding='same',
                              kernel_initializer= tf.contrib.layers.xavier_initializer(seed=2))
        ## No batch norm here
        ## leak relu here / alpha = 0.2
        relu1 = tf.maximum(alpha * x1, x1)
        # applied drop out here
        drop1 = tf.nn.dropout(relu1, keep_prob= keep_prob)
        # 14 * 14 * 128

        # Layer 2
        x2 = tf.layers.conv2d(drop1, 256, 5, strides=2, padding='same',
                             kernel_initializer= tf.contrib.layers.xavier_initializer(seed=2))
        ## employ batch norm here
        bn2 = tf.layers.batch_normalization(x2, training=True)
        ## leak relu
        relu2 = tf.maximum(alpha * bn2, bn2)
        drop2 = tf.nn.dropout(relu2, keep_prob=keep_prob)

        # 7 * 7 * 256

        # Layer3
        x3 = tf.layers.conv2d(drop2, 512, 5, strides=2, padding='same',
                             kernel_initializer= tf.contrib.layers.xavier_initializer(seed=2))
        bn3 = tf.layers.batch_normalization(x3, training=True)
        relu3 = tf.maximum(alpha * bn3, bn3)
        drop3 = tf.nn.dropout(relu3, keep_prob=keep_prob)
        # 4 * 4 * 512

        # Output
        # Flatten
        flatten = tf.reshape(relu3, (-1, 4 * 4 * 512))
        logits = tf.layers.dense(flatten,1)
        # activation
        out = tf.nn.sigmoid(logits)

    return out, logits

接着是 Generator 

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def generator(z, out_channel_dim, is_train=True):
    """
    Create the generator network
    :param z: Input z
    :param out_channel_dim: The number of channels in the output image
    :param is_train: Boolean if generator is being used for training
    :return: The tensor output of the generator
    """
    # TODO: Implement Function

    with tf.variable_scope('generator', reuse = not is_train):
        # First Fully connect layer
        x0 = tf.layers.dense(z, 4 * 4 * 512)
        # Reshape
        x0 = tf.reshape(x0,(-1,4,4,512))
        # Use the batch norm
        bn0 = tf.layers.batch_normalization(x0, training= is_train)
        # Leak relu
        relu0 = tf.nn.relu(bn0)
        # 4 * 4 * 512

        # Conv transpose here
        x1 = tf.layers.conv2d_transpose(relu0, 256, 4, strides=1, padding='valid')
        bn1 = tf.layers.batch_normalization(x1, training=is_train)
        relu1 = tf.nn.relu(bn1)
        # 7 * 7 * 256

        x2 = tf.layers.conv2d_transpose(relu1, 128, 3, strides=2, padding='same')
        bn2 = tf.layers.batch_normalization(x2, training=is_train)
        relu2 = tf.nn.relu(bn2)
        # 14 * 14 * 128

        # Last cov
        logits = tf.layers.conv2d_transpose(relu2, out_channel_dim, 3, strides=2, padding='same')
        ## without batch norm here
        out = tf.tanh(logits)


        return out

然后我们来定义loss,这里,加入了smoother

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def model_loss(input_real, input_z, out_channel_dim):
    """
    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)
    """
    # TODO: Implement Function


    g_model = generator(input_z, out_channel_dim, is_train=True)

    d_model_real, d_logits_real = discriminator(input_real, reuse = False)

    d_model_fake, d_logits_fake = discriminator(g_model, reuse= True)

    ## add smooth here

    smooth = 0.1
    d_loss_real = tf.reduce_mean(
        tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_real,
                                                labels=tf.ones_like(d_model_real) * (1 - smooth)))

    d_loss_fake = tf.reduce_mean(
        tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake, labels=tf.zeros_like(d_model_fake)))

    g_loss = tf.reduce_mean(
        tf.nn.sigmoid_cross_entropy_with_logits(logits=d_logits_fake,
                                                labels= tf.ones_like(d_model_fake)))

    d_loss = d_loss_real + d_loss_fake



    return d_loss, g_loss

接着我们需要定义网络优化的过程,这里我们需要用到batch_normlisation, 不懂的话去搜下文档

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def model_opt(d_loss, g_loss, learning_rate, 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)
    """

    t_vars = tf.trainable_variables()
    d_vars = [var for var in t_vars if var.name.startswith('discriminator')]
    g_vars = [var for var in t_vars if var.name.startswith('generator')]



    update_ops = tf.get_collection(tf.GraphKeys.UPDATE_OPS)

    with tf.control_dependencies(update_ops):
        d_train_opt = tf.train.AdamOptimizer(learning_rate,beta1=beta1).minimize(d_loss,var_list = d_vars)
        g_train_opt = tf.train.AdamOptimizer(learning_rate,beta1=beta1).minimize(g_loss,var_list = g_vars)

    return d_train_opt, g_train_opt

现在,我们网络的模块,损失函数,以及优化的过程都定义好了,现在我们就要开始训练我们的网络了,我们的训练过程定义如下。

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
def train(epoch_count, batch_size, z_dim, learning_rate, beta1, get_batches, data_shape, data_image_mode):
    """
    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")
    """
    losses = []
    samples = []

    input_real, input_z, lr = model_inputs(data_shape[1], data_shape[2], data_shape[3], z_dim)

    d_loss, g_loss = model_loss(input_real,input_z,data_shape[-1])

    d_opt, g_opt = model_opt(d_loss, g_loss, learning_rate, beta1)

    steps = 0

    with tf.Session() as sess:
        sess.run(tf.global_variables_initializer())
        for epoch_i in range(epoch_count):
            for batch_images in get_batches(batch_size):
                # TODO: Train Model
                steps += 1

                # Reshape the image and pass to Discriminator
                batch_images = batch_images.reshape(batch_size,
                                                    data_shape[1],
                                                    data_shape[2],
                                                    data_shape[3])
                # Rescale the data to -1 and 1
                batch_images = batch_images * 2

                # Sample the noise
                batch_z = np.random.uniform(-1,1,size = (batch_size, z_dim))


                ## Run optimizer
                _ = sess.run(d_opt, feed_dict = {input_real:batch_images,
                                                 input_z:batch_z,
                                                 lr:learning_rate
                                                 })
                _ = sess.run(g_opt, feed_dict = {input_real:batch_images,
                                                 input_z:batch_z,
                                                 lr:learning_rate})

                if steps % 10 == 0:

                    train_loss_d = d_loss.eval({input_real:batch_images, input_z:batch_z})
                    train_loss_g = g_loss.eval({input_real:batch_images, input_z:batch_z})

                    losses.append((train_loss_d,train_loss_g))

                    print("Epoch {}/{}...".format(epoch_i+1, epochs),
                          "Discriminator Loss: {:.4f}...".format(train_loss_d),
                          "Generator Loss: {:.4f}".format(train_loss_g))

                if steps % 100 == 0:

                    show_generator_output(sess, 25, input_z, data_shape[-1], data_image_mode)

开始训练,超参数的设置

对于MNIST

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
batch_size = 64
z_dim = 100
learning_rate = 0.001
beta1 = 0.5
epochs = 2

mnist_dataset = helper.Dataset('mnist', glob(os.path.join(data_dir, 'mnist/*.jpg')))
with tf.Graph().as_default():
    train(epochs, batch_size, z_dim, learning_rate, beta1, mnist_dataset.get_batches,
          mnist_dataset.shape, mnist_dataset.image_mode)

训练效果如下

开始的时候,网络的参数很差,我们生成的手写数字的效果自然就不好

随着训练的进行,轮廓逐渐清晰,效果如下,到最后:

我们看到数字的轮廓基本是清晰可以辨认的,当然,这只是两个epoch的结果,如果有足够的时间经过更长时间的训练,效果会更好。

我们同样展示下对celeba人脸数据集的训练结果

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
batch_size = 32
z_dim = 100
learning_rate = 0.001
beta1 = 0.4
epochs = 1

celeba_dataset = helper.Dataset('celeba', glob(os.path.join(data_dir, 'img_align_celeba/*.jpg')))
with tf.Graph().as_default():
    train(epochs, batch_size, z_dim, learning_rate, beta1, celeba_dataset.get_batches,
          celeba_dataset.shape, celeba_dataset.image_mode)

训练开始:

经过一个epoch之后:

人脸的轮廓基本清晰了。这里我们就是用了DCGAN最简单的方式来实现,原理过程说的不是很详细,同时,可能这个参数设置也不是很合理,训练的也不够成分,但是我想可以帮大家快速掌握实现一个简单的DCGAN的方法了。

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2020/09/26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
VR吃鸡一手评测:落地自带四倍镜的史上最骚“吃(zuo)鸡(bi)”神器
喜欢玩吃鸡的老司机们都知道,对于枪战类游戏来说,最重要的一点就是:视野。视野的大小作为观察敌人动态的最重要因素,对于资深玩家来说,一点点差别都将影响战局。比如在吃鸡最后的决赛圈,增加一点视野或许就决定了是否能快速识别出“老阴X”。所以视野对于枪战类游戏也显得极为重要,促生了许多大屏显示器。
重庆华哥哥
2018/06/21
1.6K0
聊一聊游戏外挂这点事
外挂是指在与游戏中不按照正常的游戏流程游戏,通过作弊的手段越过正常的游戏设定,所有的作弊的软件或脚本都是外挂。
香菜聊游戏
2021/05/26
2.4K0
聊一聊游戏外挂这点事
YVR DK1,能拯救网鱼网咖吗?
(VRPinea 1月7日讯)12月28日,玩出梦想旗下子公司YVR,正式推出旗下首款VR一体机——YVR DK1。从命名来看,这是一款面向开发者的设备,因此其3999元人民币的售价与竞品相比较,没有参考价值,P君以下发言也会抛开价格因素。
VRPinea
2022/03/11
6800
YVR DK1,能拯救网鱼网咖吗?
搭载AI的真·物理自瞄外挂,宣称不会被发现,动视:已连锅端
丰色 发自 凹非寺 量子位 报道 | 公众号 QbitAI 都说主机游戏难以开挂,最近却有人称他们实现了“全平台制霸”,无论是在PC里,还是PS、Xbox上,都可以成为“神枪手”: 在各种FPS游戏里枪枪爆头,弹无虚发。 据说还能成功躲过反作弊软件系统级的lockdown! 就问你气不气! 究其原理,这似乎是一个真·物理的自瞄外挂,只要你有另一台电脑,再加上视频采集卡、模拟输入控制器(模拟鼠标或手柄),当然还少不了核心软件AI。 AI加持的物理外挂 具体来说,这个外挂先是用一个视频采集卡记录游戏的实时
量子位
2023/03/10
1.6K0
搭载AI的真·物理自瞄外挂,宣称不会被发现,动视:已连锅端
GMGC—腾讯如何打造一款实时对战手游
最近公众号停更了一段时间,因为一直忙于GMGC2016全球移动游戏大会的腾讯游戏服务展位工作,负责演讲:腾讯游戏开发者训练营—腾讯如何打造实时对战手游。这篇推送便是此次GMGC的演讲内容。 从2015
韩伟
2018/03/05
1.8K0
GMGC—腾讯如何打造一款实时对战手游
走进 VR 游戏开发的世界
背景介绍 笔者在2014年下半年尝试开发了一款 XboxOne 平台的体感游戏, 2015年进行收尾工作的同时, 结合之前积累的体感交互经验, 开始进行 VR 游戏的预研工作. 在这近一年的时间里, 一方面从外界感受到了一股虚拟现实快速发展的潮流, 另一方面也体会到身边很多人对 VR 游戏的了解非常有限. 现在我们自己的 VR 游戏 Demo 已经完结, 技术上验证了在当前的硬件条件下, 开发高画质游戏的可行性. 当然, 优质的 VR 游戏需要大家共同的努力, 我们也希望更多的人参与进来. 在此把我们在开发
腾讯Bugly
2018/03/23
1.1K0
走进 VR 游戏开发的世界
微软腾讯合体开大!上千网红爆款涌进桌面,PC手机次元壁崩塌
你,是否曾这样设想过:如果某一天,移动应用能和Windows PC无缝集成,就太好了。
新智元
2025/03/24
1060
微软腾讯合体开大!上千网红爆款涌进桌面,PC手机次元壁崩塌
一枚程序猿的MacBook M1使用体验
2020年11月11日双十一上午,苹果发布了M1芯片的新款Mac,其最大的变化就是将处理器从Intel换成了苹果自研的ARM芯片M1。
Rude3Knife的公众号
2020/12/15
3.1K0
一枚程序猿的MacBook M1使用体验
开源FPGA硬件模拟游戏机,原汁原味的复古游戏体验带你回童年
玩腻了追求极致画面表现,玩法上却千篇一律的“罐头大作”的人们,开始怀念童年记忆中那些简单的美好。
量子位
2021/04/23
1.7K0
开源FPGA硬件模拟游戏机,原汁原味的复古游戏体验带你回童年
深度揭秘!暴利游戏外挂后面的黑色产业链
由公安部挂牌督办,一起特大“吃鸡”外挂案件被破获。警方一举捣毁6个国内的游戏点卡以及充值产品的在线交易平台,抓获犯罪嫌疑人15名,涉案金额高达3000多万元,真是大快人心!
顶级程序员
2018/07/23
2.1K0
网络游戏是如何实现对战的呢?本文告诉你
前几天和同事聊起我之前做电商交易系统时,是如何保证订单数据的最终一致性的,聊到后来,想到一个有趣的问题,显然,对战类网络游戏对于数据一致性的要求更为苛刻,这些对战类网游中,是怎么保证玩家间数据的最终一致性呢?
用户3147702
2022/06/27
2.8K0
网络游戏是如何实现对战的呢?本文告诉你
揭密微信跳一跳小游戏那些外挂
张小龙:这个游戏发布以后,其实它的效果有点超出我们的预期,我们自己开玩笑说,这个游戏突然变成了有史以来可能用户规模最大的一个游戏,因为它的DAU大概到了1点几亿,但同时出现了很多外挂,我没有想到这么小的一款游戏也会有那么多外挂,我朋友圈的朋友也打出了特别高的分,但是我相信不是他自己打出来的。
WeTest质量开放平台团队
2018/10/29
6970
15K star!推荐一款开源手机电脑投屏神器,无需root、功能强悍!
它可以通过 USB / 网络连接Android设备,并进行显示和控制,且无需root权限。
测试开发技术
2024/03/13
1.9K0
15K star!推荐一款开源手机电脑投屏神器,无需root、功能强悍!
微信跳一跳小游戏外挂分析
张小龙:这个游戏发布以后,其实它的效果有点超出我们的预期,我们自己开玩笑说,这个游戏突然变成了有史以来可能用户规模最大的一个游戏,因为它的DAU大概到了1点几亿,但同时出现了很多外挂,我没有想到这么小的一款游戏也会有那么多外挂,我朋友圈的朋友也打出了特别高的分,但是我相信不是他自己打出来的。
疯狂的小程序
2018/01/22
1.2K0
微信跳一跳小游戏外挂分析
万物皆可健身环:UP主爆改switch,用健身环玩起《塞尔达传说之旷野「喘」息》
从《塞尔达传说:旷野之息》的复苏神庙醒来的时候,我没有大师之剑,也没有海利亚之盾,更没有小绿帽。
量子位
2020/03/31
6660
万物皆可健身环:UP主爆改switch,用健身环玩起《塞尔达传说之旷野「喘」息》
揭密微信跳一跳小游戏那些外挂
本着钻研技术的学习态度,我对目前几款比较火的外挂进行了源码分析,总结出了它们的一些破解思路。
WeTest质量开放平台团队
2018/01/18
2.7K2
揭密微信跳一跳小游戏那些外挂
姗姗来迟的爱奇艺奇遇3,又是一款“雷声大雨点小”的产品?
今年1月,爱奇艺奇遇3举行了一次技术发布会,号称是国内首款量产的视觉头手6dof一体机,且要从技术上全面对标Quest 2。从当时公布的参数来看,奇遇3已经和Quest 2很接近,但这次发布会没有现场体验部分,所以其实际效果还是未知数。
VRPinea
2021/11/08
6750
姗姗来迟的爱奇艺奇遇3,又是一款“雷声大雨点小”的产品?
下一个十年,看影创科技如何开启元宇宙时代
在10月29日凌晨一点(北京时间)的Facebook Connect 2021大会上,马克·扎克伯格宣布Facebook公司正式改名为Meta,并且描绘了许多元宇宙的场景,以体现Meta拥抱元宇宙的决心。
VRPinea
2021/11/16
3970
【云+社区年度征文】一枚程序猿的MacBook M1详细体验报告
2020年11月11日双十一上午,苹果发布了M1芯片的新款Mac,其最大的变化就是将处理器从Intel换成了苹果自研的ARM芯片M1。
蛮三刀酱
2020/11/30
1.5K0
电脑玩手游全军出击和刺激战场设置攻略
玩全军出击和刺激战场的小伙伴都知道,目前腾讯这两款大火的吃鸡手游是没有PC电脑版的,用模拟器操作只能匹配模拟器玩家,很难让各位玩家快速吃鸡。最近很多玩家在用的tcgames电脑玩手游助手是可以实现用电脑鼠标键盘操作手游,并且匹配手游服玩家(目前软件是免费的),对很多玩手游的玩家来说是一个不错的选择。
知识与交流
2021/04/02
2.8K0
电脑玩手游全军出击和刺激战场设置攻略
推荐阅读
相关推荐
VR吃鸡一手评测:落地自带四倍镜的史上最骚“吃(zuo)鸡(bi)”神器
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
本文部分代码块支持一键运行,欢迎体验
本文部分代码块支持一键运行,欢迎体验