leetcodeday3—无重复字符的最长子串

给定一个字符串 s ,请你找出其中不含有重复字符的 最长子串 的长度。

示例 1:

输入: s = "abcabcbb"
输出: 3 
解释: 因为无重复字符的最长子串是 "abc",所以其长度为 3。

示例 2:

输入: s = "bbbbb"
输出: 1
解释: 因为无重复字符的最长子串是 "b",所以其长度为 1。

示例 3:

输入: s = "pwwkew"
输出: 3
解释: 因为无重复字符的最长子串是 "wke",所以其长度为 3。
     请注意,你的答案必须是 子串 的长度,"pwke" 是一个子序列,不是子串。

示例 4:

输入: s = ""
输出: 0

思路:

我第一遍写的时候,想法是把字符串作为一个队列输入,当前输入到队列 中的j in 队列时,就把队列中第一个出列,并更新maxlenght。代码如下 :

# @lc code=start
class Solution:
    def lengthOfLongestSubstring(self, x: str) -> int:
        maxlength = 0
        lens = len(x)
        for i in range(lens-1):
            for j in range(i+1,lens):
                if  x[j] in x[i:j] or  x[j]==x[i]:
                    maxlength = maxlength if maxlength>(j-i) else (j-i)
                    break
                elif j == lens-1:
                    maxlength = maxlength if maxlength>(j-i+1) else (j-i+1)
                    break
                else:
                    continue
        return maxlength

# @lc code=end

但是通过率不是100,因为没有考虑空字符串。

Wrong Answer

  • 879/987 cases passed (N/A)

Testcase

" "

可以在开始加一个判断 if ” ” in string

class Solution:
    def lengthOfLongestSubstring(self, x: str) -> int:
        maxlength = 0
        lens = len(x)
        if " " in x[0:1]  or lens==1:
            return 1
        for i in range(lens-1):
            for j in range(i+1,lens):
                if  x[j] in x[i:j] or  x[j]==x[i]:
                    maxlength = maxlength if maxlength>(j-i) else (j-i)
                    break
                elif j == lens-1:
                    maxlength = maxlength if maxlength>(j-i+1) else (j-i+1)
                    break
                else:
                    continue
        return maxlength

Accepted

  • 987/987 cases passed (520 ms)
  • Your runtime beats 8.75 % of python3 submissions
  • Your memory usage beats 16.81 % of python3 submissions (15.2 MB)

改进版:

滑动窗口
思路和算法

我们先用一个例子考虑如何在较优的时间复杂度内通过本题。

我们不妨以示例一中的字符串 \texttt{abcabcbb}abcabcbb 为例,找出从每一个字符开始的,不包含重复字符的最长子串,那么其中最长的那个字符串即为答案。对于示例一中的字符串,我们列举出这些结果,其中括号中表示选中的字符以及最长的字符串:

以 \(\texttt{(a)bcabcbb}(a)bcabcbb\) 开始的最长字符串为 \(\texttt{(abc)abcbb}(abc)abcbb\);
以 \(\texttt{a(b)cabcbb}a(b)cabcbb\) 开始的最长字符串为 \(\texttt{a(bca)bcbb}a(bca)bcbb\);
以 \(\texttt{ab(c)abcbb}ab(c)abcbb\) 开始的最长字符串为 \(\texttt{ab(cab)cbb}ab(cab)cbb\);
以 \(\texttt{abc(a)bcbb}abc(a)bcbb\) 开始的最长字符串为 \(\texttt{abc(abc)bb}abc(abc)bb\);
以 \(\texttt{abca(b)cbb}abca(b)cbb\) 开始的最长字符串为 \(\texttt{abca(bc)bb}abca(bc)bb\);
以 \(\texttt{abcab(c)bb}abcab(c)bb\) 开始的最长字符串为 \(\texttt{abcab(cb)b}abcab(cb)b\);
以 \(\texttt{abcabc(b)b}abcabc(b)b\) 开始的最长字符串为\(\texttt{abcabc(b)b}abcabc(b)b\);
以 \(\texttt{abcabcb(b)}abcabcb(b)\) 开始的最长字符串为 \(\texttt{abcabcb(b)}abcabcb(b)\)。
发现了什么?如果我们依次递增地枚举子串的起始位置,那么子串的结束位置也是递增的!这里的原因在于,假设我们选择字符串中的第 k个字符作为起始位置,并且得到了不包含重复字符的最长子串的结束位置为 r_k。那么当我们选择第 k+1 个字符作为起始位置时,首先从 k+1 到 r_k的字符显然是不重复的,并且由于少了原本的第 k个字符,我们可以尝试继续增大 r_k,直到右侧出现了重复字符为止。这样一来,我们就可以使用「滑动窗口」来解决这个问题了:

我们使用两个指针表示字符串中的某个子串(或窗口)的左右边界,其中左指针代表着上文中「枚举子串的起始位置」,而右指针即为上文中的 r_k在每一步的操作中,我们会将左指针向右移动一格,表示我们开始枚举下一个字符作为起始位置,然后我们可以不断地向右移动右指针,但需要保证这两个指针对应的子串中没有重复的字符。在移动结束后,这个子串就对应着以左指针开始的,不包含重复字符的最长子串。我们记录下这个子串的长度;

在枚举结束后,我们找到的最长的子串的长度即为答案

class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        # 哈希集合,记录每个字符是否出现过
        occ = set()
        n = len(s)
        # 右指针,初始值为 -1,相当于我们在字符串的左边界的左侧,还没有开始移动
        rk, ans = -1, 0
        for i in range(n):
            if i != 0:
                # 左指针向右移动一格,移除一个字符
                occ.remove(s[i - 1])
            while rk + 1 < n and s[rk + 1] not in occ:
                # 不断地移动右指针
                occ.add(s[rk + 1])
                rk += 1
            # 第 i 到 rk 个字符是一个极长的无重复字符子串
            ans = max(ans, rk - i + 1)
        return ans

  • 哈希Map 只需一次遍历, more efficiency
  •  Python 代码
class Solution:
    def lengthOfLongestSubstring(self, s: str) -> int:
        k, res, c_dict = -1, 0, {}
        for i, c in enumerate(s):
            if c in c_dict and c_dict[c] > k:  # 字符c在字典中 且 上次出现的下标大于当前长度的起始下标
                k = c_dict[c]
                c_dict[c] = i
            else:
                c_dict[c] = i
                res = max(res, i-k)
        return res

BicycleGAN-图像一对多转换测试

2024年 9月
 1
2345678
9101112131415
16171819202122
23242526272829
30  

CycleGAN、pix2pix、iGAN的主要贡献者最近在NIPS 2017上又推出了一篇文章Toward Multimodal Image-to-Image Translation(见https://junyanz.github.io/BicycleGAN/,https://arxiv.org/pdf/1711.11586.pdf),讨论如何从一张图像同时转换为多张风格不一成对的图像。

Pix2pix 和 CycleGAN 是非常的流行GAN,不仅在学术界有许多变体,同时也有许多基于此的应用。但是,它们都有一个缺点——图像的输出看起来几乎总是相同的。例如,如果我们要执行斑马到马的转换,被转换的同一马的照片将始终具有相同的外观和色调。这是由于GAN固有的特性,它学会过滤了噪声的随机性。

像pix2pix这样的图像转换(一对一)的方式是存在歧义的,因为不可能只对应一个输出。因此作者提出了一种一对多的输出,即将可能输出的图像是存在一定的分布特性的。

论文的主要方法如下图所示:

下图是 BicycleGAN 相关的模型和配置。图(a)是推理的配置,图像A与噪声相结合以生成图像B ^ ,可以将此看作是 cGAN 。在BicyleGAN中,形状为(256, 256, 3)的图像A是条件,而从潜在编码 z采样的噪声为大小为8的一维向量。图(b)是 pix2pix + 噪声 的训练配置。而图(c) 和 图(d) 的两个配置由 BicycleGAN 训练时使用:

简而言之,BicycleGAN 可以找到潜在编码z与目标图像B之间的关系,因此生成器可以在给定不同的z时学会生成不同的图像B ^ 。如上图所示,BicycleGAN 通过组合 cVAE-GAN 和 cLR-GAN 这两种模型来做到这一点。

cVAE-GAN
  VAE-GAN 的作者认为,L1 损失并不是衡量图像视觉质量的良好指标。例如,如果图像向右移动几个像素,则人眼看起来可能没有什么不同,但会导致较大的L1损失。因此使用 GAN 的鉴别器来学习目标函数,以判断伪造的图像是否真实,并使用 VAE 作为生成器,生成的图像更清晰。如果忽略上图(c)中的图像 A ,那就是 VAE-GAN ,由于以 A 为条件,其成为条件 cVAE-GAN 。训练步骤如下:

  • VAE 将真实图片 B编码为多元高斯分布的潜在编码,然后从它们中采样以创建噪声输入,此流程是标准的VAE工作流程;
  • 使用图像 A 作为条件及从潜矢量 z 采样的噪声用于生成伪图像B ^

训练中的数据流为 B − > z − > B ^ ( 图(c) 中的实线箭头),总的损失函数由三个损失组成:

对抗损失 \(L_{GAN}^{VAE}\)

L1​重建损失 \(L_{1}^{VAE}(G)\)

KL散度损失 \(L_{KL}(E)\)

cLR-GAN(Conditional Latent Regressor GAN)
  在 cVAE-GAN 中,对真实图像B进行编码,以提供潜在矢量的真实样本并从中进行采样。但是,cLR-GAN 的处理方式有所不同,其首先使用生成器从随机噪声中生成伪图像 B^,然后对伪图像 B^ 进行编码,最后计算其与输入随机噪声差异。
前向计算步骤如下:

首先,类似于 cGAN ,随机产生一些噪声,然后串联图像A以生成伪图像 B ^ ,之后,使用来自 VAE-GAN 的同一编码器将伪图像 B ^ 编码为潜矢量。
最后,从编码的潜矢量中采样 z ^ ,并用输入噪声 z 计算损失。数据流为 z −> B ^ −> z ^ ( 图(d) 中的实线箭头),有两个损失:

对抗损失 \(L_{GAN}\)

噪声 N(z) 与潜在编码之间的 L1损失 \(L_{1}^{latent}\)

通过组合这两个数据流,在输出和潜在空间之间得到了一个双映射循环。 BicycleGAN 中的 bi 来自双映射(双向单射),这是一个数学术语,简单来说其表示一对一映射,并且是可逆的。在这种情况下,BicycleGAN 将输出映射到潜在空间,并且类似地从潜在空间映射到输出。总损失如下:

最总的损失:

可以分为两块来理解,第一块就是cVAE-GAN的训练,我们分析的基础就是鞋子纹理风格生成为例。

鞋子纹理图片经过编码器得到编码后的latent z通过KL距离将其拉向我们事先定义好的分布N(z)上,将服从分布的z与鞋子草图A结合后送入生成器G中得到重构的鞋子纹理图。 此时为了衡量重构和真实的误差,这里用了L1损失和GAN的对抗思想实现,我们在后面损失函数分析部分再说。这样cVAE-GAN部分就可以训练了,cVAE GAN的重点还是在得到的embedding z

另一块就是cLR-GAN的训练,将鞋子草图A和分布N(z)结合经过生成器G得到鞋子纹理图, 再通过对生成的纹理图编码后得到的z去趋近分布N(z)来反向矫正生成图,达到一个变相的循环。

当这两部分训练的很好时,这个就是我们需要的BicycleGAN了,在检验训练效果时我们只需要,输入A加上N(z)就可以生成鞋子的纹理图了, 这个N(z)具体为什么怎么取将决定生成为纹理的风格了。

一些细节

  • 这里有一个小trike就是z和图片A的结合送入生成器G的结合方法,文中给出了两种方法:一种直接concat在input的channel上,一种Unet在压缩的时候,每次结果都加。 我们通过图解可以更好理解。

pytorch代码:https://github.com/junyanz/BicycleGAN

c++ 分支语句和逻辑运算符

  • if语句
  • 逻辑运算符 && ! ||
  • cctype字符函数库
  • 条件运算符:?:
  • switch语句
  • continue和break
  • 读取数字的循环
  • 基本文件输入输出

1、 if语句

if (test_condition){ body } 如果test_condition为true,那么就执行 body语句。

2、 if else 语句

if(test_condition)
    {body}
else
    {body}

3、 if else if else 语句

if(test_condition)
    {body}
else if
    {body}

else
    {body}

易错点:赋值运算符=和比较运算符==,在test_condition中使用==表达等于

逻辑表达式

1、逻辑或 ||

2、逻辑与 &&

A&&B 只有A和B都为true时,表达式才为true\

可以用来表示范围 A<20 && A> 30

3、逻辑非 !

!A

注意: 逻辑运算符 &&和||优先级都低于关系运算符,!运算符则大于所有关系运算符和算数运算符。

此外可以使用 and or not表示

字符函数库 cctype #include <cctype>

该头文件声明了一组用于对单个字符进行分类和转换的函数。

条件运算符 ?:

表达式1?表达式2:表达式3

a>1?b=2:b=4 如果a>1,那么令b=2,否则b=4

switch语句 case中的value必须是int整数

switch(interger-expression){
    case value1: 
             body;
             break;
    case value2: 
             body;
             break;
    .............
    case valuen: 
             body;
             break;
default:body
}

break和continue

break用于switch语句,跳出switch;continue用于循环语句,用于跳出本次循环。

写文本文件

读取文件

神经网络可视化工具

2024年 9月
 1
2345678
9101112131415
16171819202122
23242526272829
30  

来源:磐创AI分享

神经网络可视化工具

Convolution Visualizer

https://ezyang.github.io/convolution-visualizer/index.html

这种交互式可视化演示了各种卷积参数如何影响输入、权重和输出矩阵之间的形状和数据依赖性。将鼠标悬停在输入/输出上将突出显示相应的输出/输入,而将鼠标悬停在权重上将突出显示哪些输入与该权重相乘以计算输出。(严格来说,这里可视化的操作是相关性,而不是卷积,因为真正的卷积在执行相关性之前会翻转其权重。但是,大多数深度学习框架仍然称这些卷积,最终与梯度下降相同.)

Weights & Biases

https://docs.wandb.ai/v/zh-hans/

Weights & Biases 可以帮助跟踪你的机器学习项目。使用我们的工具记录运行中的超参数和输出指标(Metric),然后对结果进行可视化和比较,并快速与同事分享你的发现。

通过wandb,能够给你的机器学习项目带来强大的交互式可视化调试体验,能够自动化记录Python脚本中的图标,并且实时在网页仪表盘展示它的结果,例如,损失函数、准确率、召回率,它能够让你在最短的时间内完成机器学习项目可视化图片的制作。

总结而言,wandb有4项核心功能:

看板:跟踪训练过程,给出可视化结果
报告:保存和共享训练过程中一些细节、有价值的信息
调优:使用超参数调优来优化你训练的模型
工具:数据集和模型版本化
也就是说,wandb并不单纯的是一款数据可视化工具。它具有更为强大的模型和数据版本管理。此外,还可以对你训练的模型进行调优。

draw_convnet

一个用于画卷积神经网络的Python脚本

https://github.com/gwding/draw_convnet

NNSVG

http://alexlenail.me/NN-SVG/LeNet.html

PlotNeuralNet:用于为报告和演示绘制神经网络的 Latex 代码。

https://github.com/HarisIqbal88/PlotNeuralNet

Tensorboard

https://www.tensorflow.org/tensorboard/graphs

Caffe

https://github.com/BVLC/caffe/blob/master/python/caffe/draw.py

Matlab

http://www.mathworks.com/help/nnet/ref/view.html

Keras.js

https://transcranial.github.io/keras-js/#/inception-v3

DotNet

https://github.com/martisak/dotnets

Graphviz

http://www.graphviz.org/

ConX

https://conx.readthedocs.io/en/latest/index.html

ENNUI

https://math.mit.edu/ennui/

Neataptic

https://wagenaartje.github.io/neataptic/

pyTorch模型可视化

visdom:

在PyTorch深度学习中,最常用的模型可视化工具是Facebook(中文为脸书,目前已改名为Meta)公司开源的Visdom

Visdom可以直接接受来自PyTorch的张量,而不用转化成NumPy中的数组,从而运行效率很高。此外,Visdom可以直接在内存中获取数据,毫秒级刷新,速度很快。

Visdom的安装很简单,直接执行以下命令即可:

pip install visdom

开启服务,因为visdom本质上是一个类似于Jupyter Notebook 的Web服务器,在使用之前需要在终端打开服务,代码如下:

python -m visdom.server

正常执行后,根据提示在浏览器中输入相应地址即可,默认地址为:

http://localhost:8097/

实例

本例通过使用PyTorch的可视化工具Visdom对手写数字数据集进行建模。

步骤1:先导入模型需要的包,代码如下。

import torch

import torch.nn as nn

import torch.nn.functional as F

import torch.optim as optim

from torchvision import datasets, transforms

from visdom import Visdom

步骤2:定义训练参数,代码如下。

batch_size=200

learning_rate=0.01

epochs=10

… …

执行成功后,在visdom网页可以看到实时更新的训练过程的数据变化,每一个epoch测试数据更新一次,如图9-15所示。

Visdom是由Plotly 提供的可视化支持,所以提供一下可视化的接口:

  • vis.scatter : 2D 或 3D 散点图
  • vis.line : 线图
  • vis.stem : 茎叶图
  • vis.heatmap : 热力图
  • vis.bar : 条形图
  • vis.histogram: 直方图
  • vis.boxplot : 箱型图
  • vis.surf : 表面图
  • vis.contour : 轮廓图
  • vis.quiver : 绘出二维矢量场
  • vis.image : 图片
  • vis.text : 文本
  • vis.mesh : 网格图
  • vis.save : 序列化状态

更新损失函数

在训练的时候我们每一批次都会打印一下训练的损失和测试的准确率,这样展示的图表是需要动态增加数据的,下面我们来模拟一下这种情况:

x,y=0,0
env2 = Visdom()
pane1= env2.line(
    X=np.array([x]),
    Y=np.array([y]),
    opts=dict(title='dynamic data'))

Setting up a new session…

for i in range(10):
    time.sleep(1) #每隔一秒钟打印一次数据
    x+=i
    y=(y+i)*1.5
    print(x,y)
    env2.line(
        X=np.array([x]),
        Y=np.array([y]),
        win=pane1,#win参数确认使用哪一个pane
        update='append') #我们做的动作是追加

TensorBoard

pytorch也支持tensorboard的使用:

Tensorboard的使用逻辑

Tensorboard的工作流程简单来说是

  • 将代码运行过程中的,某些你关心的数据保存在一个文件夹中:
这一步由代码中的writer完成
  • 再读取这个文件夹中的数据,用浏览器显示出来:
这一步通过在命令行运行tensorboard完成。

官方:

https://pytorch.org/docs/stable/tensorboard.html?highlight=tensorboard

其中可视化的主要功能如下:

(1)Scalars:展示训练过程中的准确率、损失值、权重/偏置的变化情况。

(2)Images:展示训练过程中记录的图像。

(3)Audio:展示训练过程中记录的音频。

(4)Graphs:展示模型的数据流图,以及训练在各个设备上消耗的内存和时间。

(5)Distributions:展示训练过程中记录的数据的分部图。

(6)Histograms:展示训练过程中记录的数据的柱状图。

(7)Embeddings:展示词向量后的投影分部。

动手练习:可视化模型参数

步骤1:首先导入相关的第三方包,代码如下。

import numpy as np

from torch.utils.tensorboard import SummaryWriter

步骤2:将loss写到Loss_Accuracy路径下面,代码如下。

np.random.seed(10)

writer = SummaryWriter(‘runs/Loss_Accuracy’)

步骤3:然后将loss写到writer中,其中add_scalars()函数可以将不同的变量添加到同一个图,代码如下。

for n_iter in range(100):

writer.add_scalar(‘Loss/train’, np.random.random(), n_iter)

writer.add_scalar(‘Loss/test’, np.random.random(), n_iter)

writer.add_scalar(‘Accuracy/train’, np.random.random(), n_iter)

writer.add_scalar(‘Accuracy/test’, np.random.random(), n_iter)

代码体中要做的事

首先导入tensorboard

from torch.utils.tensorboard import SummaryWriter   

这里的SummaryWriter的作用就是,将数据以特定的格式存储到刚刚提到的那个文件夹中。

首先我们将其实例化

writer = SummaryWriter('./path/to/log')

这里传入的参数就是指向文件夹的路径,之后我们使用这个writer对象“拿出来”的任何数据都保存在这个路径之下。

这个对象包含多个方法,比如针对数值,我们可以调用

writer.add_scalar(tag, scalar_value, global_step=None, walltime=None)

这里的tag指定可视化时这个变量的名字,scalar_value是你要存的值,global_step可以理解为x轴坐标。

举一个简单的例子:

for epoch in range(100)
    mAP = eval(model)
    writer.add_scalar('mAP', mAP, epoch)

这样就会生成一个x轴跨度为100的折线图,y轴坐标代表着每一个epoch的mAP。这个折线图会保存在指定的路径下(但是现在还看不到)

同理,除了数值,我们可能还会想看到模型训练过程中的图像。

 writer.add_image(tag, img_tensor, global_step=None, walltime=None, dataformats='CHW')
 writer.add_images(tag, img_tensor, global_step=None, walltime=None, dataformats='NCHW')

可视化

我们已经将关心的数据拿出来了,接下来我们只需要在命令行运行:

tensorboard --logdir=./path/to/the/folder --port 8123

然后打开浏览器,访问地址http://localhost:8123/即可。这里的8123只是随便一个例子,用其他的未被占用端口也没有任何问题,注意命令行的端口与浏览器访问的地址同步。

如果发现不显示数据,注意检查一下路径是否正确,命令行这里注意是

--logdir=./path/to/the/folder 

而不是

--logdir= './path/to/the/folder '

另一点要注意的是tensorboard并不是实时显示(visdom是完全实时的),而是默认30秒刷新一次。

细节

1.变量归类

命名变量的时候可以使用形如

writer.add_scalar('loss/loss1', loss1, epoch)
writer.add_scalar('loss/loss2', loss2, epoch)
writer.add_scalar('loss/loss3', loss3, epoch)

的格式,这样3个loss就会被显示在同一个section。

2.同时显示多个折线图

假如使用了两种学习率去训练同一个网络,想要比较它们训练过程中的loss曲线,只需要将两个日志文件夹放到同一目录下,并在命令行运行

tensorboard --logdir=./path/to/the/root --port 8123

物体检测中小物体问题

摘自:3D视觉初学者

0.介绍

检测小物体是计算机视觉中最具挑战性和重要的问题之一。在这篇文章中,我们将讨论通过迭代数百种小物体检测模型在Roboflow上开发的一些策略。无人机在公共空中海上数据集中从上方看到的小物体

为了提高模型在小对象上的性能,我们建议以下技术入手:

  • 提高图像拍摄分辨率
  • 增加模型的输入分辨率
  • 平铺图片
  • 通过扩充生成更多数据
  • 自动学习模型
  • 过滤掉多余的类

小物件很难!例如,在EfficientDet中,小型对象的AP仅为12%,大型对象的AP为51%。那几乎是五倍的差异!那么,为什么很难检测小物体呢?一切都取决于模型。对象检测模型通过聚合卷积层中的像素来形成特征。

PP-YOLO中用于对象检测的特征聚合

如果地面物体本来就不大,而在进行训练时还会变小。因此,小物体最有可能出现数据标记错误,甚至可能会省略其标识。从经验和理论上讲,小物体都很难检测。

提高图像拍摄分辨率

分辨率,分辨率,分辨率……全都与分辨率有关。

很小的物体在边界框内可能只包含几个像素,这意味着增加图像的分辨率以增加检测器可以从该边界框提取信息的丰富度,这非常重要。因此,如果可能我们建议捕获尽可能高分辨率的图像。

增加模型的输入分辨率

获得更高分辨率的图像后,就可以扩大模型的输入分辨率。警告:这将导致大型模型需要花费较长的训练时间,并且在开始部署时将较慢地推断出来。您可能需要进行实验以找出速度与性能之间的权衡。

平铺图片

检测小图像的另一种很好的策略是将图像平铺作为预处理步骤。平铺可以有效地将检测器放大到小物体上,但可以保持所需的小输入分辨率,以便能够进行快速推理。

通过扩充生成更多数据

数据扩充会从基本数据集中生成新图像。这对于防止模型过度拟合训练集非常有用。对于小物体检测,一些特别有用的增强包括随机裁剪,随机旋转和镶嵌增强。自动学习模型的锚定框

锚定框是模型学习预测的原型边界框。也就是说,锚框可以预先设置,有时对于训练数据而言不是最佳的。自定义调整这些参数以适合我们需要完成的任务,这是很好的。YOLOv5模型架构会根据小伙伴的自定义数据自动为您完成此操作。我们要做的就只是是开始训练。

过滤掉多余的类

类管理是提高数据集质量的一项重要技术。如果有两个类明显重叠的类,则应从数据集中过滤一个。如果因为认为数据集中的小对象不值得检测,并希望将他们先去除。大家可以通过运行Roboflow Pro的高级数据集来快速识别所有这些小对象。可以通过Roboflow的管理工具来实现类遗漏和类重命名。

c++ 循环和关系表达式

1、for循环

for循环主要完成以下部分:1、设置初始值 2、判断循环是否继续进行 3、执行循环操作 4、更新用于测试的值

for(初始值;测试条件;更新测试值)//初始值可以 自定义变量   int x = 0

for循环中更新值:i++ i– 也可以选择不同步长 i = i+2 i=i+4

2、递增运算符 ++ 和 递减运算符 —

a++ :先使用a,在a自增 ++a :先a自加一,在使用a

for循环语句块:

字符串的比较:

string类字符串比较:

3、while循环

while(循环条件){
body
}

4、类型别名

方法一:

方法二:

5、do while 循环

基于范围的循环 (C++11 新增)

用于数组或者容器类,如vector 和 array。

for ( int x : array) { body }

如果要在循环体对 x进行修改:for (int &x : arrayname) 需要使用引用。

循环和文本输入:

1、使用原始cin输入:

补救:使用cin.get(char):会读取空格,并赋给char

文件尾条件 EOF

二维数组

初始化:

使用二维数组: maxtemps[1][2]

GAN系列之 StarGAN

StarGAN: Unified Generative Adversarial Networks for Multi-Domain Image-to-Image Translation

Authors

Yunjey Choi, Minje Choi, Munyoung Kim, Jung-Woo Ha, Sunghun Kim, Jaegul Choo

Abstract

Recent studies have shown remarkable success in image-to-image translation for two domains. However, existing approaches have limited scalability and robustness in handling more than two domains, since different models should be built independently for every pair of image domains. To address this limitation, we propose StarGAN, a novel and scalable approach that can perform image-to-image translations for multiple domains using only a single model. Such a unified model architecture of StarGAN allows simultaneous training of multiple datasets with different domains within a single network. This leads to StarGAN’s superior quality of translated images compared to existing models as well as the novel capability of flexibly translating an input image to any desired target domain. We empirically demonstrate the effectiveness of our approach on a facial attribute transfer and a facial expression synthesis tasks.

Pix2Pix模型解决了有Pair对数据的图像翻译问题;CycleGAN解决了Unpaired数据下的图像翻译问题。但无论是Pix2Pix还是CycleGAN,都是解决了一对一的问题,即一个领域到另一个领域的转换。当有很多领域要转换了,对于每一个领域转换,都需要重新训练一个模型去解决。这样的行为太低效了。本文所介绍的StarGAN就是将多领域转换用统一框架实现的算法。

下图是StarGAN的效果,在同一种模型下,可以做多个图像翻译任务,比如更换头发颜色,更换表情,更换年龄等。

StarGAN,顾名思义,就是星形网络结构,在StarGAN中,生成网络G被实现成星形。

1.CycleGAN 不能解决多领域迁移的问题。 只能两个领域的互相转化A->B,B-A。 但是实际场景中,我们可能遇到 多个数据集,或者多种属性的互相转化的要求。这样的话我们就需要O(n^2)的G model。(如下图)

2.有些属性(如人的表情),如果只取其中的两个属性(笑和不笑),那么就无法利用上其他训练数据(比如生气/恐惧等表情数据)。

1.作者提出了StarGAN 来处理多个domain之间互相generate图像 的问题。只用一个generator网络。

假如想实现四个域内图像风格的相互转换,要实现这个目标,通过cycleGAN需要创建12个生成器(如图a)。而starGAN的直观构造如图b,只需要一个生成器即可。

2. G的输入除了图片,还有domain的label,对应的把生成图片变到指定的domain。

starGAN的提出是为了解决多数据集在多域间图像转换的问题,starGAN可以接受多个不同域的训练数据,并且只需要训练一个生成器,就可以拟合所有可用域中的数据。

StarGAN的大致训练流程

i)如图a,训练判别器,将 real_img 和 fake_img 分别传递给判别器,判别器会判别图像的真假,同时它还会判别该图像来自哪个域(只对real_img 的label做判别)。
ii)如图b,训练生成器,与CGAN类似,这里除了输入图像外,还要输入该图像想转换的目标域,这个目标域类似于约束条件,它要求生成器尽可能去生成该目标域中的图像。
iii)如图c,表示循环一致性的过程,如果只是单纯的使用条件去控制生成器生成,那么生成器就会生成满足条件但可能与输入图像无关的数据,为了避免这种情况,便使用循环一致性的思想,即将生成的图像加上输入图像所在的域作为生成器的输入,希望获得的输出与原输入图像越接近越好。
iiii)如图d,表示训练生成器,即将生成器生成的图片交给判别器,让判别器判别图像的真假以及图像所在的域是否正确。

损失函数:

Adversarial loss 为 conditional gan常用的。(实际替换为WGAN的loss)

Reconstruction loss 为L1 Loss (和Cyclegan一样)

Domain classification loss(属性分类)就是传统分类log NLLloss。

类别损失,该损失被分成两个,训练D的时候,使用真实图像在原始领域进行,训练G的时候,使用生成的图像在目标领域进行。

多数据集训练
在多数据集下训练StarGAN存在一个问题,那就是数据集之间的类别可能是不相交的,但内容可能是相交的。比如CelebA数据集合RaFD数据集,前者拥有很多肤色,年龄之类的类别。而后者拥有的是表情的类别。但前者的图像很多也是有表情的,这就导致前一类的图像在后一类的标记是不可知的。

为了解决这个问题,在模型输入中加入了Mask,即如果来源于数据集B,那么将数据集A中的标记全部设为0.

GAN系列之 CycleGAN

摘自 https://zhuanlan.zhihu.com/p/306442363

pixtopix需要一对一,一个image对应一个image,训练集的两组图片一一对应才能训练

CycleGAN的介绍

1.CycleGAN的原理

CycleGAN,即循环生成对抗网络,出自发表于 ICCV17 的论文《Unpaired Image-to-Image Translation using Cycle-Consistent Adversarial Networks》,和它的兄长Pix2Pix(均为朱大神作品)一样,用于图像风格迁移任务。以前的GAN都是单向生成,CycleGAN为了突破Pix2Pix对数据集图片一一对应的限制,采用了双向循环生成的结构,因此得名CycleGAN。

首先,CycleGAN也是一个GAN模型,通过判别器和生成器的对抗训练,学习数据集图片的像素概率分布来生成图片。

要完成X域到Y域的图片风格迁移,就要求GAN网络既要拟合Y域图片的风格分布分布,又要保持X域图片对应的内容特征。打个比方,用草图风格的猫图片生成照片风格的猫图片时,要求生成的猫咪“即要活灵活现,又要姿势不变”。“拟合数据分布”本来就是GAN干的活儿,而“保持原图片特征”在Pix2Pix上是这么实现的:

因为Pix2Pix是一个CGAN,所以,我们通过用X域图片当约束条件来限制Pix2Pix的输出Y域风格图片时保有X域图片的特征。

而送入CycleGAN的两组(X域Y域)图片没有一一对应关系,即使我们将X域图片当成限制条件输入到一个CGAN中,也起不到限制模型输出保有X域图片特征的作用。因为,送入的两组图片完全是随机配在一起,CGAN学不到任何联系。因此,CycleGAN采取了一个绝妙的设计:通过添加“循环生成”并优化一致性损失(Consistency Loss)来代替CGAN中使用的约束条件来限制生成器保有原域图片特征。这样就不需要训练集图片一一对应了。

2.CycleGAN的流程

下面,我们就来看看循环生成网络(CycleGAN)到底是怎么“循环起来”的:

上图左半部分,将原域图片x送入(x2y方向)生成器G生成目标域图片y^,然后再将生成的目标域图片y^送入(y2x方向)生成器F反过来生成原域图片x^。生成x^的目的就是用它与输入的真图片x来算L1 Loss。我们知道Pix2Pix优化时除了使用GAN Loss(对抗损失)外,还加入了生成器输入图片和输出图片的L1 Loss来对齐生成图片与输入图片的宏观轮廓(所谓低频信息)。同样的逻辑,我们也能在CycleGAN中用L1 Loss来对齐“循环生成”的x^与输入的原图片x的内容自然,x生成的y^的轮廓也是和x对齐的了。这就达到了(原论文中的例子)“马变斑马,花纹变,姿势不变”的目的了。(我在网上看到的CycleGAN资料都没有点明这一点的,所以只好自行脑补,欢迎指正。)

在这个x->y^->x^的生成过程中,可以通过判别器Dy与生成器(x2y)G进行对抗训练。那么这个链条上的反向生成器(y2x)F怎么办?当然是加个判别器Dx与它进行对抗训练了。这样CycleGAN就有了两个方向相反的生成器,两个分别判别x域、y域图片的判别器。但要注意一个问题:就像GAN的生成器和判别器不能同时训练一样,Cyc1eGAN的两个生成器、两个判别器也只能一个一个训练,这就形成了CycleGAN训练的两条“环路”。

3.CycleGAN的结构

接下来,我们再看看这两对判别器、生成器怎么摆:

上半部份是生成器G和判别器Dy进行x2y的训练过程,下半部份是生成器F和判别器Dx进行y2x的训练过程。很像是两个风格迁移方向相反Pix2Pix模型,只是这两个GAN是普通GAN,不是Pix2Pix那样的CGAN。这一点,从生成器和判别器的输入就可以看出来,输入的只有原域图片并没有像Pix2Pix一样融合条件图片。

4.CycleGAN的loss函数

前面分析了CycleGAN的原理,我们已经知道了CycleGAN的loss由对抗损失(称为gan loss或adversarial loss)和循环一致性损失(consitency loss)组成,下面看看公式:

上面公式中:

​指的是x2y过程的对抗损失(adversarial loss)

​指的是y2x过程的对抗损失(adversarial loss)

​指的是生成器G和生成器F的循环一致性损失。

其中为循环一致性损失(consitency loss)的缩放系数,是一个超参数。

实际上,原论文的代码还加入了本体映射损失(identity loss),只是默认设置为关闭。CycleGAN正常训练时,生成器G输入x,生成y^。计算生成器G的本体映射损失(identity loss)时,生成器G输入y,生成y^,然后用y与y^的L1 loss作为G的identity loss。相应地,生成器F的identity loss则是输入的x与生成的x^的L1 loss。优化CycleGAN时,如果启用identity loss则将这两部分加到模型总loss中。与循环一致性损失(consistency loss)一样,也使用缩放系数超参控制其在总loss中所占比重。

论文中提到,CycleGAN使用identity loss的目的是在迁移的过程中保持原色调,下面是使用identity loss的对比效果:

上面图片最右边一列使用identity loss后果然纠正了生成器的色偏。

code:

https://github.com/eriklindernoren/PyTorch-GAN

GAN系列之pix2pix

也许是CycleGAN的光芒太过耀眼,Pix2Pix就像家中的次子,还没得宠多长时间,就被弟弟CycleGAN抢走了风头。这也怪不得它们的“爹滴”朱大神把“域风格迁移”的CycleGAN(下个项目介绍)造得太好用了,似乎完全能够代替“像素风格迁移”的Pix2Pix,以至于都来不及给Pix2Pix起个××GAN的名字~

其实,除了“白天照片变夜晚”、“图片着色”、“蓝图变街景”等它弟弟CycleGAN更容易玩儿的花样儿外,Pix2Pix是有着自己的独门绝技的。比如,用自然风景照片训练好的Pix2Pix模型,能实时将手绘的草图渲染成对应风景照片。如果训练集照片里包括老虎等动物,我们几笔在一个圆圈脑袋上画个王字,Pix2Pix模型就能生成一张活灵活现的大老虎,比《照相馆的故事》快多了~Pix2Pix的工作也启发了一些更具体的应用,比如专门手绘照片的SketchyGAN、手绘人脸的模型DeepFaceDrawing等。另外Pix2Pix->Pix2PixHD(高清渲染)->Vid2Vid(视频实时渲染)也是一条发展路线。试想,只需建好游戏人物和场景的结构模型,然后机器自动按训练的风格渲染人物和场景,游戏设计师们有没有感到点儿激动。

1. Pix2Pix的原理

发表在CVPR2017上的论文《Image-to-Image Translation with Conditional Adversarial Networks》是将GAN应用于有监督的图像到图像翻译的经典论文,提出的GAN模型被简称为Pix2Pix(不叫××GAN,很像是小名儿吧~)。为了解决图像到图像的翻译(也就是前面提到的那些上色、手绘草图的应用),我们需要建立一个模型实现图像到图像的映射。

以前曾经有过尝试搭建一个CNN网络进行映射,并用L1距离来度量、优化模型,结果发现效果很模糊(用L2距离更模糊),就像下面这样:

那么,既然GAN能够较好地生成图片的细节,我们何不拿来一用?显然,经典GAN是不行的,没法控制输出嘛。CGAN正好拿来一用。对此,朱大神在报告里曾经解释过:如果我们用经典GAN,判别器判别时会出现这样的问题。

这样的生成图片判别为真没问题

但是,这样的生成图片也判断为真就有问题了。显而易见,生成的猫图片与手绘的猫草图的形态完全不一致。但因为这也是一张猫图片,是符合训练集图片的像素概率分布的,所以会被经典GAN判别为真图片。

为了解决这一问题,我们将输入的猫草图作为“条件标签”和生成的猫图片一起送入判别器进行判断,如下图:

这看上去是不是有点儿CGAN的影子?没错,这个Pix2Pix就是个CGAN!

2.Pix2Pix的结构

我们将Pix2Pix的结构与上篇CGAN的结构对比一下:

上图的上半部份是普通CGAN的结构,下半部分是Pix2Pix的结构。对比发现,Pix2Pix与CGAN的结构有两点不同:

  1. 在Pix2Pix中,输入生成器的控制条件由“分类标签y”变成了A组(原风格)图片,因为这里我们要用A组(原风格)图片做为控制条件来生成B组(目标风格)图片。由于输入生成器的A组图片的维度(图片尺寸)与生成器输出的B组图片的维度相同,足以映射复杂分布,所以,我们不必再输入噪声z。细心的同学可能会发现:在刚才那张“对比普通CGAN和Pix2Pix结构”的图片中,我们对“条件y”的解释,与上一张“介绍给Pix2Pix加标签原因”的图片中的解释不一样。“对比结构”的图片中将生成器的输入解释为“条件y”,而“解释用CGAN原因”的图片中将生成器的输入解释为“输入x”。实际上这两种对生成器输入的解释都指的是A(原风格)组图片,不影响后面的推理。但个人觉得:将生成器的输入解释为“条件y”更容易帮助理解Pix2Pix的CGAN本质。我理解,Pix2Pix拟合的是训练集中B组(目标风格)图片的像素概率分布,A组(原风格)图片是作为“约束条件”来使用的。对比一下普通CGAN的结构就清楚了。
  2. 在Pix2Pix中,输入判别器的控制条件也由“分类标签y”变成了A组(原风格)图片。A组(原风格)图片作为“条件y”要和真B组(目标风格)图片或生成器生成的假B组图片(在图像通道维度上)拼接在一起送入判别器。这个很好理解,也说明了前面把生成器的输入解释为“条件y”更“工整”。

这样,Pix2Pix做了以上改动后,整个模型从“输入噪声、输出图片”的流程,变成了“输入A组图片、输出B组图片”的流程。

3.Pix2Pix的loss

在大神造Pix2Pix的过程中也试过各种“配方”。包括使用L1损失、使用CGAN损失和使用两者之和,测试结果如下:

观察结果发现:

  • 只用L1损失时,生成的图片比较模糊。
  • 只用CGAN损失时,生成的图片很清晰,但颜色风格与Ground Truth图片差别较大。
  • 使用L1+CGAN损失时,生成的图片又清晰,又保留了更多Ground Truth图片的特征。

所以,最后Pix2Pix使用了L1+CGAN损失。我们看下loss的构成。

先看L1损失:

L1损失的计算方法就是真B组(目标风格)图片与生成器生成的假B组图片逐像素求差的绝对值再求平均。公式中的x指A组(原风格)图片,y指B组(目标风格)图片,z指C输入给生成器的(一般是高斯分布的)噪声,代码中并未使用。

再来看看CGAN损失:

Pix2Pix的CGAN损失和普通CGAN损失一模一样

Pix2Pix总的损失是这两者之和:

GAN系列之CGAN(Conditional GAN)

GAN只是拟合原数据集的像素概率分布,生成的样本并没有提供新的信息以优化模型的分类边界。我理解,样本插值还能优化一下分类边界,原始GAN充其量只能添加一点噪声,或许能增强一点模型泛化能力吧(真做数据增强还得InforGAN、styleGAN这样的才好,能通过潜空间插值对图像做高级语义的增强,这是后话。)。

原始GAN用起来也不方便,为了分别生成0~9的数字,得将原数据集按标签分为10组,每组用一个模型训练,一共需要10个模型。训练时由于每组的数据量少到原来的十分之一,也会发生因样本太少导致模型无法拟合的现象。所以,意欲降伏GAN的大神给原始GAN装了个钮,让GAN乖乖要啥给啥。这个带按钮的改进版就是CGAN。

CGAN(Conditional GAN)介绍

1、CGAN的原理

CGAN的全称是Conditional Generative Adversarial Nets,即条件生成对抗网络。故名思议,就是通过添加限制条件,来控制GAN生成数据的特征(类别)。

当我第一次了解了CGAN原理,我惊诧于它给GAN“加按钮”的方法竟然如此简单粗暴,要做仅仅就是“把按钮加上去”——训练时将控制生成类别的标签连同噪声一起送进生成器的输入端,这样在预测时,生成器就会同样根据输入的标签生成指定类别的图片了判别器的处理也是一样,仅仅在输入加上类别标签就可以了。

那么,为什么加了标签,CGAN就乖乖听话、要啥给啥了呢?原理也是十分简单,我们知道GAN要干的就是拟合数据的概率分布,而CGAN拟合的就是条件下的概率分布。

GAN:

原生GAN中的概率全改成条件概率:

而上面CGAN公式中的条件y就是咱给GAN装的“钮”。加上了这个条件按钮,GAN优化的概率期望分布公式就变成了CGAN优化的条件概率期望分布公式。即CGAN优化的目标是:在条件Y下,在判别器最大化真实数据与生成数据差异的情况下,最小化这个差距。训练CGAN的生成器时要同时送入随机噪声z和和条件y(在本项目中y就是MNIST手写数字数据集的数字标签)。就是这么简单!

2、CGAN的结构

CGAN设计巧妙,而结构也十分简单、清晰,与经典GAN只有输入部分稍许不同。

我们看看原始GAN与CGAN的结构对比(包括生成器和判别器),上半部份的是经典GAN,下半部分是CGAN:

我们先回顾下经典GAN的结构流程(如上图上半部份所示):

  • 训练判别器。将噪声z送入生成器,输出fake_x;将fake_x送入判别器,在更新判别器参数时尝试拉近判别器的输出与真标签1的距离,即最小化判别器输出与真标签1的交叉熵损失。再将真图片送入判别器,更新判别器参数时尝试拉近判别器的输出与假标签0的距离,即最小化判别器输出与假标签0的交叉熵损失。这个过程中,用真、“假”图片训练判别器的顺序不必需固定,真、假标签取值0、1也无需固定(可相反,效果没有区别)。要注意的是,训练判别器的过程中,只更新判别器参数,不更新生成器参数。
  • 训练生成器。生成器训练的过程和判别器基本一样,只是将生成器输出的“假图片”送入判别器后,将判别器的输出与真标签(1)拉近。目的就是,使生成器参数更新的方向朝着“骗过判别器的目标”进行,也就是所谓“对抗过程”。当然判别器出掌(判别器更新参数)时,生成器不还手(生成器不更新参数),轮到生成器还手(生成器更新参数)时,判别器也得双手背后(判别器不更新参数)。不然就打成一团,谁也看不到招式(无法正确更新参数,提高生成能力)了

我们再看下CGAN给GAN加的“料”(如上图下半部份所示):

  • 先看判别器。如图,无论是给判别器送入真图片还是生成器生成的假图片时,都要加上个“条件y”,也就是分类标签。判别器输出没有变化仍然只是判断输入图片的真假。老实说,当时我曾想:既然咱都conditional GAN了,这个判别器是不是要输出分类标签y来训练Condition那部分?但转念一想,不行,判别器还是得判别真假,不然没法和生成器对抗了。BUT,后来我发现还真有走这个路线的GAN,叫InfoGAN。这个InfoGAN给生成器配了两个判别器,一个判真假,一个分类别。
  • 再看生成器。生成器的输入除了随机噪声z外,也加入了“条件y”。到这儿,我又想:既然有了条件标签,就不用输入噪声z了吧~。答案当然是,不行!因为,噪声z的维度是和生成器输出图片的尺寸、复杂度相关的。本项目中输出图片尺寸是28×28=784。按理说模型进行映射的输入、输出尺寸应该是相等的。但是输出图片只是手写数字,规律比较简单,输入的尺寸可以进行一定程度的压缩。一般噪声z的维度为几十到一百就能生成比较理想的图片细节,如果太低会导致生成器拟合能力不足,生成图片质量低下。条件z只是一个取值0~9的维度为一的向量,模型拟合像素概率分布的效果可想而知。后面我们介绍的Pix2Pix模型的输入是一张和输出尺寸相同的图片,就不再输入噪声z了。

CGAN需要注意的一点是:输入的条件标签y不但要在输入时与噪声z融合在一起,在生成器和判别器的每一层输入里都要与特征图相融合,才能让模型“学好条件y”。不然,标签可能不灵~

code https://github.com/eriklindernoren/PyTorch-GAN