Pytorch 中 model.eval() model.train() 和 with torch.no_grad() 的区别

1、model.eval() model.train()区别

model.train()和model.eval()的区别主要在于Batch Normalization和Dropout两层。

官方文档 model.train()
启用 Batch Normalization 和 Dropout。
如果模型中有BN层(Batch Normalization)和 Dropout,需要在训练时添加model.train()model.train()是保证BN层能够用到每一批数据的均值和方差。对于Dropout,model.train()是随机取一部分网络连接来训练更新参数。

官方文档 model.eval()
不启用 Batch Normalization 和 Dropout。
如果模型中有BN层(Batch Normalization)和Dropout,在测试时添加model.eval()model.eval()是保证BN层能够用全部训练数据的均值和方差,即测试过程中要保证BN层的均值和方差不变。对于Dropout,model.eval()是利用到了所有网络连接,即不进行随机舍弃神经元。

训练完train样本后,生成的模型model要用来测试样本。在model(test)之前,需要加上model.eval(),否则的话,有输入数据,即使不训练,它也会改变权值。这是model中含有BN层和Dropout所带来的的性质。

在做one classification的时候,训练集和测试集的样本分布是不一样的,尤其需要注意这一点。

2 . model.eval()和with torch.no_grad()的区别:


在PyTorch中进行validation时,会使用model.eval()切换到测试模式,在该模式下,

主要用于通知dropout层和batchnorm层在train和val模式间切换
在train模式下,dropout网络层会按照设定的参数p设置保留激活单元的概率(保留概率=p); batchnorm层会继续计算数据的mean和var等参数并更新。
在val模式下,dropout层会让所有的激活单元都通过,而batchnorm层会停止计算和更新mean和var,直接使用在训练阶段已经学出的mean和var值。
该模式不会影响各层的gradient计算行为,即gradient计算和存储与training模式一样,只是不进行反传(backprobagation)


with torch.no_grad()则主要是用于停止autograd模块的工作,以起到加速和节省显存的作用,具体行为就是停止gradient计算,从而节省了GPU算力和显存,但是并不会影响dropout和batchnorm层的行为。


使用场景:
如果不在意显存大小和计算时间的话,仅仅使用model.eval()已足够得到正确的validation的结果;而with torch.zero_grad()则是更进一步加速和节省gpu空间(因为不用计算和存储gradient),从而可以更快计算,也可以跑更大的batch来测试。

Python装饰器:python中的@符号的作用 以及 torch中经常出现的 @torch.no_grad()

@符号是装饰器(修饰符)的语法糖,在定义函数的时候使用,避免再一次赋值操作

装饰器(Decorators)是 Python 的一个重要部分。简单地说:他们是修改其他函数的功能的函数。他们有助于让我们的代码更简短,也更Pythonic(Python范儿)。大多数初学者不知道在哪儿使用它们,所以我将要分享下,哪些区域里装饰器可以让你的代码更简洁。 首先,让我们讨论下如何写你自己的装饰器。

‘@’符号用作函数修饰符是python2.4新增加的功能,修饰符必须出现在函数定义前一行,不允许和函数定义在同一行。也就是说@A def f(): 是非法的。只可以在模块或类定义层内对函数进行修饰,不允许修饰一个类。一个修饰符就是一个函数,它将被修饰的函数做为参数,并返回修饰后的同名函数或其它可调用的东西。

实例(1):

def spamrun(fn):
   def sayspam(*args):
       print("spam,spam,spam")
   return sayspam
@spamrun
def useful(a,b):
   print (a**2+b**2)

执行: useful(3,4)

返回:spam,spam,spam

def addspam(fn):
   def new(*args):
       print "spam,spam,spam"
       return fn(*args)
   return new

@addspam
def useful(a,b):
   print a**2+b**2

执行: useful(4,3)

结果:

spam,spam,spam

25

@torch.no_grad()

@torch.no_grad()
def eval():
	...

@torch.no_grad()后面的函数的数据不需要计算梯度,也不会进行反向传播

Python装饰器:

装饰器本质上是一个Python函数,它可以让其他函数在不需要做任何代码变动的前提下增加额外功能,装饰器的返回值也是一个函数对象。它经常用于有切面需求的场景,比如:插入日志、性能测试、事务处理、缓存、权限校验等场景。装饰器是解决这类问题的绝佳设计,有了装饰器,我们就可以抽离出大量与函数功能本身无关的雷同代码并继续重用。概括的讲,装饰器的作用就是为已经存在的对象添加额外的功能。

先来看一个简单例子:

def foo():
    print('i am foo')

现在有一个新的需求,希望可以记录下函数的执行日志,于是在代码中添加日志代码:

def foo():
    print('i am foo')
    logging.info("foo is running")

bar()、bar2()也有类似的需求,怎么做?再写一个logging在bar函数里?这样就造成大量雷同的代码,为了减少重复写代码,我们可以这样做,重新定义一个函数:专门处理日志 ,日志处理完之后再执行真正的业务代码

def use_logging(func):
    logging.warn("%s is running" % func.__name__)
    func()

def bar():
    print('i am bar')

use_logging(bar)

逻辑上不难理解, 但是这样的话,我们每次都要将一个函数作为参数传递给use_logging函数。而且这种方式已经破坏了原有的代码逻辑结构,之前执行业务逻辑时,执行运行bar(),但是现在不得不改成use_logging(bar)。那么有没有更好的方式的呢?当然有,答案就是装饰器。

简单装饰器

def use_logging(func):

    def wrapper(*args, **kwargs):
        logging.warn("%s is running" % func.__name__)
        return func(*args, **kwargs)
    return wrapper

def bar():
    print('i am bar')

bar = use_logging(bar)
bar()

函数use_logging就是装饰器,它把执行真正业务方法的func包裹在函数里面,看起来像bar被use_logging装饰了。在这个例子中,函数进入和退出时 ,被称为一个横切面(Aspect),这种编程方式被称为面向切面的编程(Aspect-Oriented Programming)。

@符号是装饰器的语法糖,在定义函数的时候使用,避免再一次赋值操作

方法一:不用语法糖@符号​​​​​​​

# 装饰器不传入参数时
f = decorator(函数名)

# 装饰器传入参数时
f = (decorator(参数))(函数名)


方法二:采用语法糖@符号​​​​​​​

# 已定义的装饰器
@decorator 
def f():  
    pass

# 执行被装饰过的函数 
f()
def use_logging(func):

    def wrapper(*args, **kwargs):
        logging.warn("%s is running" % func.__name__)
        return func(*args)
    return wrapper

@use_logging
def foo():
    print("i am foo")

@use_logging
def bar():
    print("i am bar")

bar()

如上所示,这样我们就可以省去bar = use_logging(bar)这一句了,直接调用bar()即可得到想要的结果。如果我们有其他的类似函数,我们可以继续调用装饰器来修饰函数,而不用重复修改函数或者增加新的封装。这样,我们就提高了程序的可重复利用性,并增加了程序的可读性。

装饰器在Python使用如此方便都要归因于Python的函数能像普通的对象一样能作为参数传递给其他函数,可以被赋值给其他变量,可以作为返回值,可以被定义在另外一个函数内。

带参数的装饰器

装饰器还有更大的灵活性,例如带参数的装饰器:在上面的装饰器调用中,比如@use_logging,该装饰器唯一的参数就是执行业务的函数。装饰器的语法允许我们在调用时,提供其它参数,比如@decorator(a)。这样,就为装饰器的编写和使用提供了更大的灵活性。

def use_logging(level):
    def decorator(func):
        def wrapper(*args, **kwargs):
            if level == "warn":
                logging.warn("%s is running" % func.__name__)
            return func(*args)
        return wrapper

    return decorator

@use_logging(level="warn")
def foo(name='foo'):
    print("i am %s" % name)

foo()

上面的use_logging是允许带参数的装饰器。它实际上是对原有装饰器的一个函数封装,并返回一个装饰器。我们可以将它理解为一个含有参数的闭包。当我 们使用@use_logging(level=”warn”)调用的时候,Python能够发现这一层的封装,并把参数传递到装饰器的环境中。

类装饰器

再来看看类装饰器,相比函数装饰器,类装饰器具有灵活度大、高内聚、封装性等优点。使用类装饰器还可以依靠类内部的__call__方法,当使用 @ 形式将装饰器附加到函数上时,就会调用此方法。

__call__方法 : 在生成一个类的实例时,自动自行一次call方法

当执行Foo时候生成一个实例,就会自动调用__call__方法

class Foo(object):
    def __init__(self, func):
    self._func = func

def __call__(self):
    print ('class decorator runing')
    self._func()
    print ('class decorator ending')

@Foo
def bar():
    print ('bar')

bar()

functools.wraps

使用装饰器极大地复用了代码,但是他有一个缺点就是原函数的元信息不见了,比如函数的docstring、__name__、参数列表,先看例子:

装饰器

def logged(func):
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging

函数

@logged
def f(x):
   """does some math"""
   return x + x * x

该函数完成等价于:

def f(x):
    """does some math"""
    return x + x * x
f = logged(f)

不难发现,函数f被with_logging取代了,当然它的docstring,__name__就是变成了with_logging函数的信息了。

print f.__name__    # prints 'with_logging'
print f.__doc__     # prints None

这个问题就比较严重的,好在我们有functools.wraps,wraps本身也是一个装饰器,它能把原函数的元信息拷贝到装饰器函数中,这使得装饰器函数也有和原函数一样的元信息了。

from functools import wraps
def logged(func):
    @wraps(func)
    def with_logging(*args, **kwargs):
        print func.__name__ + " was called"
        return func(*args, **kwargs)
    return with_logging

@logged
def f(x):
    """does some math"""
    return x + x * x

print f.__name__  # prints 'f'
print f.__doc__   # prints 'does some math'

内置装饰器

@staticmathod、@classmethod、@property

@property

把类内方法当成属性来使用,必须要有返回值,相当于getter;

假如没有定义 @func.setter 修饰方法的话,就是只读属性

class Car:

    def __init__(self, name, price):
        self._name = name
        self._price = price    
     
    @property
    def car_name(self):
        return self._name
        
     # car_name可以读写的属性   
     @car_name.setter
     def car_name(self, value):
         self._name = value
         
     # car_price是只读属性 
     @property
     def car_price(self):
         return str(self._price) + '万'
         
benz = Car('benz', 30)

print(benz.car_name)   # benz
benz.car_name = "baojun"
print(benz.car_name)   # baojun
print(benz.car_price)  # 30万

@staticmethod

静态方法,不需要表示自身对象的self和自身类的cls参数,就跟使用函数一样。

静态方法的使用场景:

如果在方法中不需要访问任何实例方法和属性,纯粹地通过传入参数并返回数据的功能性方法,那么它就适合用静态方法来定义,它节省了实例化对象的开销成本,往往这种方法放在类外面的模块层作为一个函数存在也是没问题的,而放在类中,仅为这个类服务。

@classmethod

类方法,不需要self参数,但第一个参数需要是表示自身类的cls参数。

类方法的使用场景有:

作为工厂方法创建实例对象,例如内置模块 datetime.date 类中就有大量使用类方法作为工厂方法,以此来创建date对象。如果希望在方法里面调用静态类,那么把方法定义成类方法是合适的,因为要是定义成静态方法,那么你就要显示地引用类A,这对继承来说可不是一件好事情。

例子

class Demo(object):

    text = "三种方法的比较"
    
    def instance_method(self):
        print("调用实例方法")

    @classmethod
    def class_method(cls):
        print("调用类方法")
        print("在类方法中 访问类属性 text: {}".format(cls.text))
        print("在类方法中 调用实例方法 instance_method: {}".format(cls().instance_method()))

    @staticmethod
    def static_method():
        print("调用静态方法")
        print("在静态方法中 访问类属性 text: {}".format(Demo.text))
        print("在静态方法中 调用实例方法 instance_method: {}".format(Demo().instance_method()))

if __name__ == "__main__":
    # 实例化对象
    d = Demo()
    
    # 对象可以访问 实例方法、类方法、静态方法
    # 通过对象访问text属性
    print(d.text)
    
    # 通过对象调用实例方法
    d.instance_method()
    
    # 通过对象调用类方法
    d.class_method()
    
    # 通过对象调用静态方法
    d.static_method()
    
    # 类可以访问类方法、静态方法
    # 通过类访问text属性
    print(Demo.text)
    
    # 通过类调用类方法
    Demo.class_method()
    
    # 通过类调用静态方法
    Demo.static_method()

@staticmethod 和 @classmethod 的 区别 和 使用场景

在上述例子中,我们可以看出,

区别

在定义静态类方法和类方法时,@staticmethod 装饰的静态方法里面,想要访问类属性或调用实例方法,必须需要把类名写上;

@classmethod装饰的类方法里面,会传一个cls参数,代表本类,这样就能够避免手写类名的硬编码。

在调用静态方法和类方法时,实际上写法都差不多,一般都是通过 类名.静态方法() 或 类名.类方法()。也可以用实例对象调用类方法和静态方法。 对象可以访问 实例方法、类方法、静态方法 , 类可以访问类方法、静态方法

也可以用实例化对象去调用静态方法和类方法但为了和实例方法区分,最好还是用类去调用静态方法和类方法。

使用场景

所以,在定义类的时候,

假如不需要用到与类相关的属性或方法时,就用静态方法@staticmethod

假如需要用到与类相关的属性或方法,然后又想表明这个方法是整个类通用的,而不是对象特异的,就可以使用类方法@classmethod

装饰器的顺序

@a
@b
@c
def f ():

等效于

f = a(b(c(f)))

小红书推荐系统 —公开课

推荐系统课件:https://github.com/wangshusen/RecommenderSystem

工业界的推荐系统

这门课程结合小红书的业务场景和内部实践,讲解主流的工业界推荐系统技术。

  1. 概要
  2. 召回
  3. 排序
  4. 交叉结构
  5. 用户行为序列建模
    • 用户行为序列特征
    • DIN 模型
    • SIM 模型
  6. 重排
    • 多样性 MMR
    • 多样性 DPP
    • 多样性 MGS
  7. 物品冷启动

Real-ESRGAN 超分辨网络

论文:Real-ESRGAN: TrainingReal-World Blind Super-Resolution with Pure Synthetic Data

代码:https://github.com/xinntao/Real-ESRGAN

Real-ESRGAN 的目标是开发出实用的图像/视频修复算法。
在 ESRGAN 的基础上使用纯合成的数据来进行训练,以使其能被应用于实际的图片修复的场景(顾名思义:Real-ESRGAN)。

  1. 目标:解决真实场景下的图像模糊问题。
  2. 数据集的构建:模糊核、噪声、尺寸缩小、压缩四种操作的随机顺序。
  3. 超分网络backbone:ESRGAN的生成网络+U-Net discriminator判别器。
  4. 损失函数:L1 loss,perceptual loss,生成对抗损失。
  5. 主要对比方法是:RealSR、ESRGAN、BSRGAN、DAN、CDC。

创新点

  1. 提出了新的构建数据集的方法,用高阶处理,增强降阶图像的复杂度。
  2. 构造数据集时引入sinc filter,解决了图像中的振铃和过冲现象。
  3. 替换原始ESRGAN中的VGG-discriminator,使用U-Net discriminator,以增强图像的对细节上的对抗学习。
  4. 引入spectral normalization以稳定由于复杂数据集和U-Net discriminator带来的训练不稳定情况。

数据集构建

在讨论数据集的构建前,作者详细讨论了造成图像模糊的原因,例如:年代久远的手机、传感器噪声、相机模糊、图像编辑、图像在网络中的传输、JPEG压缩以及其它噪声。原文如下:

For example, when we take a photo with our cellphones, the photos may have several degradations, such as camera blur, sensor noise, sharpening artifacts, and JPEG compression. We then do some editing and upload to a social media APP, which introduces further compression and unpredictable noises.

所以作者针对以上问题,提出了High-order降级模型。先面我们先介绍first-order降级模型,然后就很好理解High-order降级模型了。

First-order

First-order降级模型其实就是常规的降级模型,如上式所示,按顺序执行上述操作。

x代表降级后的图像,D代表降级函数,y代表原始图像;
k代表模糊核,r代表缩小比例,n代表加入的噪声,JPEG代表进行压缩。

每一种降级方法又有多种降级方案可以选择,如下图所示:

对于模糊核k,本方法使用各项同性(isotropic)和各向异性(anisotropic)的高斯模糊核。关于sinc filter会在下文中提到。

对于缩小操作r,常用的方法又双三次插值、双线性插值、区域插值—由于最近邻插值需要考虑对齐问题,所以不予以考虑。在执行缩小操作时,本方法从提到的3种插值方式中随机选择一种。

对于加入噪声操作n,本方法同时加入高斯噪声和服从泊松分布的噪声。同时,根据待超分图像的通道数,加入噪声的操作可以分为对彩色图像添加噪声和对灰度图像添加噪声。

JPEG压缩,本方法通过从[0, 100]范围中选择压缩质量,对图像进行JPEG压缩,其中0表示压缩后的质量最差,100表示压缩后的质量最好。JPEG压缩方法点此处

  • High-order

First-order由于使用相对单调的降级方法,其实很难模仿真实世界中的图像低分辨模糊情况。因此,作者提出的High-order其实是为了使用更复杂的降级方法,更好的模拟真实世界中的低分辨模糊情况,从而达到更好的学习效果。一阶降级模型构建的数据集训练结果如下:

高阶降级模型公式如下:

上式,其实就是对First-order进行多次重复操作,也就是每一个D都是执行一次完整的First-order降级。作者通过实验得出,当执行2次First-order时生成的数据集训练效果最好。所以,High-order的pipeline如下:

  • sinc filter

为了解决超分图像的振铃和过冲现象(振铃过冲在图像处理中很常见,此处不过多介绍),作者提出了在构建降级模型中增加sinc filter的操作。先来看一下振铃和过冲伪影的效果:

上图表示实际中的振铃和过冲伪影现象,下图表示通过对sinc filter设置不同的因子人工模仿的振铃和过冲伪影现象。过于如何构造sinc filter,详细细节建议看原文。

sinc filter在两个位置进行设置,一是在每一阶的模糊核k的处理中,也就是在各项同性和各项异性的高斯模糊之后,设置sinc filter;二是在最后一阶的JPEG压缩时,设置sinc filter,其中最后一阶的JPEG和sinc filter执行先后顺序是随机的。

网络结构

  • 生成网络

生成网络是ESRGAN的生成网络,基本没变,只是在功能上增加了对x2和x1倍的图像清晰度提升。对于x4倍的超分辨,网络完全按照ESRGAN的生成器执行;而对于X2和X1倍的超分辨,网络先进行pixel-unshuffle(pixel-shuffl的反操作,pixel-shuffle可理解为通过压缩图像通道而对图像尺寸进行放大),以降低图像分辨率为前提,对图像通道数进行扩充,然后将处理后的图像输入网络进行超分辨重建。举个例子:对于一幅图像,若只想进行x2倍放大变清晰,需先通过pixel-unshuffle进行2倍缩小,然后通过网络进行4倍放大。

对抗网络

由于使用的复杂的构建数据集的方式,所以需要使用更先进的判别器对生成图像进行判别。之前的ESRGAN的判别器更多的集中在图像的整体角度判别真伪,而使用U-Net 判别器可以在像素角度,对单个生成的像素进行真假判断,这能够在保证生成图像整体真实的情况下,注重生成图像细节。

  • 光谱标准正则化

通过加入这一操作,可以缓和由于复杂数据集合复杂网络带来的训练不稳定问题。

训练

分为两步:

  1. 先通过L1 loss,训练以PSRN为导向的网络,获得的模型称为Real-ESRNet。
  2. 以Real-ESRNet的网络参数进行网络初始化,损失函数设置为 L1 loss、perceptual loss、 GAN loss,训练最终的网络Real-ESRGAN。

ESRGAN图像超分辨

论文:https://arxiv.org/abs/1809.00219

github: https://github.com/xinntao/ESRGAN

ESRGAN: Enhanced Super-Resolution Generative Adversarial Networks发表于ECCV 2018 的 Workshops,在SRGAN的基础上进行了改进,包括改进网络的结构,判决器的判决形式,以及更换了一个用于计算感知域损失的预训练网络

超分辨率生成对抗网络(SRGAN)是一项开创性的工作,能够在单一图像超分辨率中生成逼真的纹理。这项工作发表于CVPR 2017,

文章链接:Photo-Realistic Single Image Super-Resolution Using a Generative Adversarial Network

但是,放大后的细节通常伴随着令人不快的伪影。为了更进一步地提升视觉质量,作者仔细研究了SRGAN的三个关键部分:1.网络结构 2.对抗性损失 3.感知域损失;并对每一项进行改进,得到ESRGAN。具体而言,文章提出了一种Residual-in-Residual Dense Block (RRDB)的网络单元,在这个单元中,去掉了BN(Batch Norm)层。此外,作者借鉴了relativistic GAN的想法,让判别器预测图像的真实性而不是图像“是否是fake图像”。最后,文章对感知域损失进行改进,使用激活前的特征,这样可以为亮度一致性和纹理恢复提供更强的监督。在这些改进的帮助下,ESRGAN得到了更好的视觉质量以及更逼真和自然的纹理。

在纹理和细节上,ESRGAN都优于SRGAN

SRGAN的思考与贡献

现有的超分辨率网络在不同的网络结构设计以及训练策略下,超分辨的效果得到了很大的提升,特别是PSNR指标。但是,基于PSNR指标的模型会倾向于生成过度平滑的结果,这些结果缺少必要的高频信息。PSNR指标与人类观察者的主观评价从根本上就不统一。

一些基于感知域信息驱动的方法已经提出来用于提升超分辨率结果的视觉质量。例如,感知域的损失函数提出来用于在特征空间(instead of 像素空间)中优化超分辨率模型;生成对抗网络通过鼓励网络生成一些更接近于自然图像的方法来提升超分辨率的质量;语义图像先验信息用于进一步改善恢复的纹理细节。

通过结合上面的方法,SRGAN模型极大地提升了超分辨率结果的视觉质量。但是SRGAN模型得到的图像和GT图像仍有很大的差距。

ESRGAN的改进

文章对这三点做出改进:1.网络的基本单元从基本的残差单元变为Residual-in-Residual Dense Block (RRDB);2.GAN网络改进为Relativistic average GAN (RaGAN);3.改进感知域损失函数,使用激活前的VGG特征,这个改进会提供更尖锐的边缘和更符合视觉的结果。

网络结构及思想

生成器部分

首先,作者参考SRResNet结构作为整体的网络结构,SRResNet的基本结构如下:

SRResNet基本结构

为了提升SRGAN重构的图像的质量,作者主要对生成器G做出如下改变:1.去掉所有的BN层;2.把原始的block变为Residual-in-Residual Dense Block (RRDB),这个block结合了多层的残差网络和密集连接。

如下图所示:

RRDB

思想:

BN层的影响

对于不同的基于PSNR的任务(包括超分辨率和去模糊)来说,去掉BN层已经被证明会提高表现和减小计算复杂度。BN层在训练时,使用一个batch的数据的均值和方差对该batch特征进行归一化,在测试时,使用在整个测试集上的数据预测的均值和方差。当训练集和测试集的统计量有很大不同的时候,BN层就会倾向于生成不好的伪影,并且限制模型的泛化能力。作者发现,BN层在网络比较深,而且在GAN框架下进行训练的时候,更会产生伪影。这些伪影偶尔出现在迭代和不同的设置中,违反了对训练稳定性能的需求。所以为了稳定的训练和一致的性能,作者去掉了BN层。此外,去掉BN层也能提高模型的泛化能力,减少计算复杂度和内存占用。

Trick:

除了上述的改进,作者也使用了一些技巧来训练深层网络:1.对残差信息进行scaling,即将残差信息乘以一个0到1之间的数,用于防止不稳定;2.更小的初始化,作者发现当初始化参数的方差变小时,残差结构更容易进行训练。

判别器部分

除了改进的生成器,作者也基于Relativistic GAN改进了判别器。判别器 D 使用的网络是 VGG 网络,SRGAN中的判别器D用于估计输入到判别器中的图像是真实且自然图像的概率,而Relativistic判别器则尝试估计真实图像相对来说比fake图像更逼真的概率。

具体而言,作者把标准的判别器换成Relativistic average Discriminator(RaD),所以判别器的损失函数定义为:

求均值的操作是通过对mini-batch中的所有数据求平均得到的,xf是原始低分辨图像经过生成器以后的图像。

可以观察到,对抗损失包含了xr和xf,所以这个生成器受益于对抗训练中的生成数据和实际数据的梯度,这种调整会使得网络学习到更尖锐的边缘和更细节的纹理。

感知域损失

文章也提出了一个更有效的感知域损失,使用激活前的特征(VGG16网络)。

感知域的损失当前是定义在一个预训练的深度网络的激活层,这一层中两个激活了的特征的距离会被最小化。与此相反,文章使用的特征是激活前的特征,这样会克服两个缺点。第一,激活后的特征是非常稀疏的,特别是在很深的网络中。这种稀疏的激活提供的监督效果是很弱的,会造成性能低下;第二,使用激活后的特征会导致重建图像与GT的亮度不一致。

使用激活前与激活后的特征的比较,(a)亮度;(b)细节

作者对使用的感知域损失进行了探索。与目前多数使用的用于图像分类的VGG网络构建的感知域损失相反,作者提出一种更适合于超分辨的感知域损失,这个损失基于一个用于材料识别的VGG16网络(MINCNet),这个网络更聚焦于纹理而不是物体。尽管这样带来的增益很小,但作者仍然相信,探索关注纹理的感知域损失对超分辨至关重要。

损失函数

经过上面对网络模块的定义和构建以后,再定义损失函数,就可以进行训练了。

对于生成器G,它的损失函数为:

代码解析:

https://zhuanlan.zhihu.com/p/54473407?utm_id=0

3.提取感知域损失的网络(Perceptual Network)

文章使用了一个用于材料识别的VGG16网络(MINCNet)来提取感知域特征,定义如下:

class MINCNet(nn.Module):
    def __init__(self):
        super(MINCNet, self).__init__()
        self.ReLU = nn.ReLU(True)
        self.conv11 = nn.Conv2d(3, 64, 3, 1, 1)
        self.conv12 = nn.Conv2d(64, 64, 3, 1, 1)
        self.maxpool1 = nn.MaxPool2d(2, stride=2, padding=0, ceil_mode=True)
        self.conv21 = nn.Conv2d(64, 128, 3, 1, 1)
        self.conv22 = nn.Conv2d(128, 128, 3, 1, 1)
        self.maxpool2 = nn.MaxPool2d(2, stride=2, padding=0, ceil_mode=True)
        self.conv31 = nn.Conv2d(128, 256, 3, 1, 1)
        self.conv32 = nn.Conv2d(256, 256, 3, 1, 1)
        self.conv33 = nn.Conv2d(256, 256, 3, 1, 1)
        self.maxpool3 = nn.MaxPool2d(2, stride=2, padding=0, ceil_mode=True)
        self.conv41 = nn.Conv2d(256, 512, 3, 1, 1)
        self.conv42 = nn.Conv2d(512, 512, 3, 1, 1)
        self.conv43 = nn.Conv2d(512, 512, 3, 1, 1)
        self.maxpool4 = nn.MaxPool2d(2, stride=2, padding=0, ceil_mode=True)
        self.conv51 = nn.Conv2d(512, 512, 3, 1, 1)
        self.conv52 = nn.Conv2d(512, 512, 3, 1, 1)
        self.conv53 = nn.Conv2d(512, 512, 3, 1, 1)

    def forward(self, x):
        out = self.ReLU(self.conv11(x))
        out = self.ReLU(self.conv12(out))
        out = self.maxpool1(out)
        out = self.ReLU(self.conv21(out))
        out = self.ReLU(self.conv22(out))
        out = self.maxpool2(out)
        out = self.ReLU(self.conv31(out))
        out = self.ReLU(self.conv32(out))
        out = self.ReLU(self.conv33(out))
        out = self.maxpool3(out)
        out = self.ReLU(self.conv41(out))
        out = self.ReLU(self.conv42(out))
        out = self.ReLU(self.conv43(out))
        out = self.maxpool4(out)
        out = self.ReLU(self.conv51(out))
        out = self.ReLU(self.conv52(out))
        out = self.conv53(out)
        return out

再引入预训练参数,就可以进行特征提取:

class MINCFeatureExtractor(nn.Module):
    def __init__(self, feature_layer=34, use_bn=False, use_input_norm=True, \
                device=torch.device('cpu')):
        super(MINCFeatureExtractor, self).__init__()

        self.features = MINCNet()
        self.features.load_state_dict(
            torch.load('../experiments/pretrained_models/VGG16minc_53.pth'), strict=True)
        self.features.eval()
        # No need to BP to variable
        for k, v in self.features.named_parameters():
            v.requires_grad = False

    def forward(self, x):
        output = self.features(x)
        return output

网络插值思想

为了平衡感知质量和PSNR等评价值,作者提出了一个灵活且有效的方法—网络插值。具体而言,作者首先基于PSNR方法训练的得到的网络G_PSNR,然后再用基于GAN的网络G_GAN进行finetune。

然后,对这两个网络相应的网络参数进行插值得到一个插值后的网络G_INTERP:

这样就可以通过 α 值来调整效果

训练细节

训练细节

放大倍数:4,mini-batch:16

通过Matlab的bicubic函数对HR图像进行降采样得到LR图像。

HR patch大小:128×128(实验发现使用大的patch时,训练一个深层网络效果会更好,因为一个增大的感受域会帮助模型捕捉更具有语义的信息)

训练过程:

1.训练一个基于PSNR指标的模型(L1 Loss)

初始化学习率:2×1e-4

每200000个mini-batch学习率除以2

2.以1中训练的模型作为生成器的初始化

λ=5×10−3,η=0.01,β=0.2 (残差scaling系数)

初始学习率:1e-4,并在50k,100k,200k,300k迭代后减半。

一个基于像素损失函数进行优化的预训练模型会帮助基于GAN的模型生成更符合视觉的结果,原因如下:1.可以避免生成器不希望的局部最优;2.再预训练以后,判别器所得到的输入图像的质量是相对较好的,而不是完全初始化的图像,这样会使判别器更关注到纹理的判别。

优化器:Adam( β1=0.9,β2=0.999 );交替更新生成器和判别器,直到收敛。

生成器的设置:1.16层(基本的残差结构) 2.23层(RDDB)

数据集:DIV2K,Flickr2K,OST;(有丰富纹理信息的数据集会是模型产生更自然的结果)

可以看到,ESRGAN得到的图像PSNR值不高,但是从视觉效果上看会更好,Percpetual Index值更小(越小越好),而且ESRGAN在 PIRM-SR 竞赛上也获得了第一名(在Percpetual Index指标上)。

经过实验以后,作者得出结论:

1.去掉BN:并没有降低网络的性能,而且节省了计算资源和内存占用。而且发现当网络变深变复杂时,带BN层的模型更倾向于产生影响视觉效果的伪影。

2.使用激活前的特征:得到 的图像的亮度更准确,而且可以产生更尖锐的边缘和更丰富的细节。

3.RaGAN:产生更尖锐的边缘和更丰富的细节。

4.RDDB:更加提升恢复得到的纹理(因为深度模型具有强大的表示能力来捕获语义信息),而且可以去除噪声。

网络插值实验

为了平衡视觉效果和PSNR等性能指标,作者对网络插值参数 α 的取值进行了实验,结果如下:

总结

文章提出的ESRGAN在SRGAN的基础上做出了改进,包括去除BN层,基本结构换成RDDB,改进GAN中判别器的判别目标,以及使用激活前的特征构成感知域损失函数,实验证明这些改进对提升输出图像的视觉效果都有作用。此外,作者也使用了一些技巧来提升网络的性能,包括对残差信息的scaling,以及更小的初始化。最后,作者使用了一种网络插值的方法来平衡输出图像的视觉效果和PSNR等指标值。

SRGAN —使用GAN进行图像高分辨的开山之作

论文: https://arxiv.org/abs/1609.04802(CVPR 2017)

这篇文章第一次将将生成对抗网络用在了解决超分辨率问题上。将GAN引入SR领域

之前超分的研究虽然主要聚焦于“恢复细粒度的纹理细节”这个问题上,但将问题一直固定在最大化峰值信噪比(Peak Signal-to-Noise Ratio, PSNR)上,等价于 最小化与GT图像的均方重建误差(mean squared reconstruction error, MSE)。

而这也就导致:

  1. 高频细节(high-frequency details) 的丢失,整体图像过于平滑/模糊;
  2. 与人的视觉感知不一致,超分图像的精确性与人的期望不匹配(人可能更关注前景,而对背景清晰度要求不高)。
中间蓝色框是基于MSE所学到的超分图像所在像素空间,红色框是真实超分图像所在的像素空间流形,基于GAN的方法驱动重构图像往真实图像像素流形区域靠近,从而感知上更真实可信

从而提出3个改进:

  1. 新的backbone:SRResNet;
  2. GAN-based network 及 新的损失函数:
  3. adversarial loss:提升真实感(photo-realistic natural images);
  4. content loss:获取HR image和生成图像的感知相似性(perceptual similarity),而不只是像素级相似性(pixel similarity);或者说特征空间的相似性而不是像素空间的相似性。
  5. 使用主观评估手段:MOS,更加强调人的感知。

SRGAN算法改进细节:

生成网络是新的结构SRResNet(横跨主干网络的skip connection操作很关键),卷积核尺寸k,输出通道数n,步长s。头部后续接了两个输出通道数为256=64*4的卷积块,因为其中PixelShuffle*2会将feature map转化 (64, H*2, W*2)的输出(sub-pixel convolution操作),这样总共upscale *4
  • SRResNet和GAN-based Network

上图就是新的网络结构,G网络是SRResNet,论文使用了16个residual blocks;D网络为8次卷积操作(4次步长为2)+2次全连接层的VGG网络。

损失函数

生成网络的损失函数为:

包含:

论文对VGG高层特征和低层特征分别做了实验,最终选择可能关注更多图像内容的高层特征作为论文实验的损失特征图。

判别网络的损失函数为二分类交叉熵损失函数:

SRGAN实验设置

使用数据集Set5,Set14,BSD100,BSD300测试集对训练模型进行实验评估: 4×分辨率超分,然后对图像的每个边界移除4个像素点,最后center-cropped计算PSNR和SSIM,进行有效性统计分析。

随机采样ImageNet数据集中350张图像进行训练(参考源码):

SRGAN实验结果及分析

消融实验说明:

  1. skip-connection结构的有效性;
  2. PSNR体现不出人的感知(MOS);
  3. GAN-based Network能更好捕捉一些人的感知细节(高频信息?),MOS更高;
  4. VGG特征重建也有助于捕捉图像的部分感知细节。

GitHub 上的高仿项目

以下文章来源于逛逛GitHub ,作者老逛

老逛整理了现在比较热门 App 的高仿项目,这些项目都是有「recently updated」,而不是年代久远不再维护的项目。包括高仿微信、微博、B站、斗鱼、抖音、美团、头条、掘金等等。这些项目涉及的技术栈有 Vue、Flutter 等等,看这些高仿项目的源码有什么用呢?比如下面这个高仿知乎,采用 Flutter 技术栈,把代码 Download 下来,看着代码做一遍基本可以入门 Flutter,学会 APP 布局。高仿知乎使用 Flutter 模仿的知乎黑色主题的 UI,在 我的 页中可以修改日间和夜间模式。

GitHub地址:https://github.com/xujiyou/zhihu-flutter

高仿微信已实现的功能包括:消息界面、发现界面、好友搜索、聊天界面等。微信中常用功能都支持,比如:好友列表、好友搜索、好友资料、资料设置、新的朋友、群聊、标签、好友圈、扫一扫、摇一摇、漂流瓶、购物、游戏等等。至于聊天功能,可以调节字体大小、更改聊天背景、我的表情、清空聊天记录。智慧城表情键盘(动态增删表情包)、文字消息、图片消息、表情消息、语音消息、聊天输入框、消息展示视图、聊天键盘、聊天记录存储等。

GitHub地址:https://github.com/tbl00c/TLChat

高仿炸弹特效
微信 8.0 来临,新版本微信中最吸引眼球的莫过于「表情特效」,尤其是超级震撼的「炸弹」更是把微信群炸上了天。版本刚刚发布不就,就有前端大佬复现了「炸弹」特效,大佬折腾了许久,做出来的效果如下。不到 300 行代码,并且已经开源,教程和代码可以关注微信公众号「逛逛GitHub」回复「炸弹」获取代码。

仿美团外卖点餐前端用 vue+vuex+vue-router+axios,后端基于nodej.s的框架,数据库采用mongodb。功能涉及登录,定位,浏览商品,加购物车,下订单,支付(微信、支付宝扫码支付),评价,个人信息更改。

https://github.com/zwStar/vue-meituan

精仿今日头条数据是抓取今日头条 App 的数据。使用 RxJava + Retrofit + MVP 开发的开源项目。

https://github.com/chaychan/TouTiao

高仿掘金仿照掘金客户端使用 Vue 开发的 WebApp。里面所有的API均来自官方Android。页面基本上和掘金App差不多,不过由于里面的一些图片本菜使用的是阿里的iconfont,因此会和掘金App上面的图片有点不一样,但是整体的功能和掘金App还是差不多的。

地址:https://github.com/sanfengliao/vue-juejin

高仿微博iOS 版:

Github 地址:https://github.com/sam408130/DSLolita

Android:

Github 地址:https://github.com/wenmingvs/WeiBo

iOS 仿抖音这个抖音 Demo 适配 iPhone、iPad,同时兼容 iOS 8.0 – iOS 12.0系统。采用 Object-C 语言编写。标星 1.5K Star,项目地址:

https://github.com/sshiqiao/douyin-ios-objectc

本项目共分为三个部分:抖音个人主页实现、网络视频相关功能实现、WebSocket 实现 IM 即时聊天功能。

Android 仿抖音这个 Demo 涉及的技术要点如下:

  • Recycler + PagerSnapHelper 实现全屏切换播放效果,
  • 使用 Lottie 库加载 Json 动画
  • BottomSheetDialogFragment 实现分享评论弹框功能
  • CoordinatorLayout + AppBarLayout 实现折叠布局。

该项目标星 1K Star,项目地址:

https://github.com/18380438200/Tiktok

高仿 YouTubeiOS版本,Github 地址:

https://github.com/aslanyanhaik/youtube-iOS

安卓版本,Github 地址:

https://github.com/TeamNewPipe/NewPi

仿斗鱼直播 APPflutter 重构的斗鱼直播 APP,首页、娱乐为Material组件;直播间、鱼吧为纯自定义编写。GitHub 地址:

https://github.com/yukilzw/dy_flutter

仿网易云音乐基于 flutter 的仿网易云音乐软件,支持  iOS 和 Android。GitHub 地址:

https://github.com/boyan01/flutter-netease-music

高仿 B站基于 react 和 express 开发的,高仿B站 Web 移动端,GitHub 项目:

https://github.com/code-mcx/react-bilibili

高仿Bilibili,iOS 版 Github 地址:

https://github.com/MichaelHuyp/Bilibili_Wuxianda

Android 版本 Github 地址:

https://github.com/HotBitmapGG/bilibili-android-client

联邦图机器学习

近年来,图已被广泛应用于表示和处理很多领域的复杂数据,如医疗、交通运输、生物信息学和推荐系统等。图机器学习技术是获取隐匿在复杂数据中丰富信息的有力工具,并且在像节点分类和链接预测等任务中,展现出很强的性能。
尽管图机器学习技术取得了重大进展,但大多数都需要把图数据集中存储在单机上。然而,随着对数据安全和用户隐私的重视,集中存储数据变的不安全和不可行。图数据通常分布在多个数据源(数据孤岛),由于隐私和安全的原因,从不同的地方收集所需的图数据变的不可行。
例如一家第三方公司想为一些金融机构训练图机器学习模型,以帮助他们检测潜在的金融犯罪和欺诈客户。每个金融机构都拥有私有客户数据,如人口统计数据以及交易记录等。每个金融机构的客户形成一个客户图,其中边代表交易记录。由于严格的隐私政策和商业竞争,各个机构的私有客户数据无法直接与第三方公司或其它他机构共享。同时,机构之间也可能有关联,这可以看作是机构之间的结构信息。因此面临的主要挑战是:在不直接访问每个机构的私有客户数据的情况下,基于私有客户图和机构间结构信息,来训练用于金融犯罪检测的图机器学习模型。
联邦学习(FL)是一种分布式机器学习方案,通过协作训练解决数据孤岛问题。它使参与者(即客户)能够在不共享其私有数据的情况下联合训练机器学习模型。因此,将 FL 与图机器学习相结合成为解决上述问题的有希望的解决方案。
本文中,来自弗吉尼亚大学的研究者提出联邦图机器学习(FGML,Federated Graph Machine Learning)。一般来说,FGML 可以根据结构信息的级别分为两种设置:
第一种是具有结构化数据的 FL,在具有结构化数据的 FL 中,客户基于其图数据协作训练图机器学习模型,同时将图数据保留在本地。
第二种是结构化 FL,在结构化 FL 中,客户端之间存在结构信息,形成客户端图。可以利用客户端图设计更有效的联合优化方法。

论文地址:https://arxiv.org/pdf/2207.11812.pdf
虽然 FGML 提供了一个有前景的蓝图,但仍存在一些挑战:
1、跨客户端的信息缺失。在具有结构化数据的 FL 中,常见的场景是每个客户端机器都拥有全局图的子图,并且一些节点可能具有属于其他客户端的近邻。出于隐私考虑,节点只能在客户端内聚合其近邻的特征,但无法访问位于其它客户端上的特征,这导致节点表示不足。
2、图结构的隐私泄漏。在传统 FL 中,不允许客户端公开其数据样本的特征和标签。在具有结构化数据的 FL 中,还应考虑结构信息的隐私。结构信息可以通过共享邻接矩阵直接公开,也可以通过传输节点嵌入间接公开。
3、跨客户端的数据异构性。与传统 FL 中数据异构性来自 non-IID 数据样本不同,FGML 中的图数据包含丰富的结构信息。同时,不同客户的图结构也会影响图机器学习模型的性能。 4、参数使用的策略。在结构化 FL 中,客户端图使客户端能够从其相邻客户端获取信息。在结构化 FL 中,需要设计有效的策略,以充分利用由中心服务器协调或完全分散的近邻信息。
为了应对上述挑战,研究人员开发了大量算法。目前各种算法主要关注标准 FL 中的挑战和方法,只有少数人尝试解决 FGML 中的具体问题和技术。有人发表对 FGML 进行分类的综述性论文,但没有总结 FGML 中的主要技术。而有的综述文章仅涵盖了 FL 中数量有限的相关论文,并非常简要地介绍了目前现有的技术。

而在今天介绍的这篇论文中,作者首先介绍 FGML 中两种问题设计的概念。然后,回顾了每种 shezhi 下的最新的技术进展,还介绍了 FGML 的实际应用。并对可用于 FGML 应用的可访问图数据集和平台进行总结。最后,作者给出了几个有前途的研究方向。文章的主要贡献包括:
FGML 技术分类:文章给出了基于不同问题的 FGML 分类法,并总结了每个设置中的关键挑战。
全面的技术回顾:文章全面概述了 FGML 中的现有技术。与现有其它综述性论文相比,作者不仅研究了更广泛的相关工作,而且提供了更详细的技术分析,而不是简单地列出每种方法的步骤。
实际应用:文章首次总结 FGML 的实际应用。作者根据应用领域对其进行分类,并介绍每个领域中的相关工作。
数据集和平台:文章介绍了 FGML 中现有的数据集和平台,对于想在 FGML 中开发算法和部署应用程序的工程师和研究人员非常有帮助。
未来方向:文章不仅指出了现有方法的局限性,而且给出了 FGML 未来的发展方向。

这里对文章的主要结构做下简介。第 2 节简要介绍了图机器学习中的定义以及 FGML 中两种设置的概念和挑战。第 3 节和第 4 节回顾了这两种设置中的主流技术。第 5 节进一步探讨了 FGML 在现实世界中的应用。第 6 节介绍了相关 FGML 论文中使用的开放图数据集和 FGML 的两个平台。在第 7 节中提供了未来可能的发展方向。最后第 8 节对全文进行了总结。
更多详细信息请参考原论文。

用语言模型学习表示蛋白质的功能特性

以数据为中心的方法已被用于开发用于阐明蛋白质未表征特性的预测方法;然而,研究表明,这些方法应进一步改进,以有效解决生物医学和生物技术中的关键问题,这可以通过更好地代表手头的数据来实现。新的数据表示方法主要从在自然语言处理方面取得突破性改进的语言模型中汲取灵感。最近,这些方法已应用于蛋白质科学领域,并在提取复杂的序列-结构-功能关系方面显示出非常有希望的结果。在这项研究中,土耳其中东科技大学(Middle East Technical University)的研究人员,首先对每种方法进行分类/解释,然后对它们的预测性能进行基准测试,对蛋白质表示学习进行了详细调查:(1)蛋白质之间的语义相似性,(2)基于本体的蛋白质功能,(3)药物靶蛋白家族和(4)突变后蛋白质-蛋白质结合亲和力的变化。这项研究的结论将有助于研究人员将基于机器/深度学习的表示技术应用于蛋白质数据以进行各种预测任务,并激发新方法的发展。该研究以「Learning functional properties of proteins with language models」为题,于 2022 年 3 月 21 日发布在《Nature Machine Intelligence》。

蛋白质科学是一门广泛的学科,它通过实验室实验(即蛋白质组学)和计算方法(例如分子建模、机器学习、数据科学)分析单个蛋白质以及生物体的整个蛋白质组,最终创建准确且可重复使用的方法用于生物医学和生物技术。蛋白质信息学可以定义为蛋白质科学的计算和以数据为中心的分支,通过它对蛋白质的定量方面进行建模。蛋白质的功能表征对于开发新的有效的生物医学策略和生物技术产品至关重要。截至 2021 年 5 月,UniProt 蛋白质序列和注释知识库中约有 2.15 亿条蛋白质条目;然而,其中只有 56 万份(约 0.26%)由专家手动审查和注释,这表明当前的排序(数据生产)和注释(标签)能力之间存在很大差距。这种差距主要是由于从湿实验室实验及其手动管理中获得结果的成本较高,同时具有时间密集性。为了补充基于实验和管理的注释,使用计算机方法势在必行。在这种情况下,许多研究小组一直致力于开发新的计算方法来预测蛋白质的酶活性、生物物理特性、蛋白质和配体相互作用、三维结构以及最终的功能。蛋白质功能预测(PFP)可以定义为自动或半自动地将功能定义分配给蛋白质。生物分子功能的主要术语被编入基因本体论(GO)系统;这是一个概念的分层网络,用于注释基因和蛋白质的分子功能,以及它们的亚细胞定位和它们所涉及的生物过程。PFP 最全面的基准项目是功能注释的关键评估(CAFA)挑战;在该项目中,参与者预测一组目标蛋白的基于 GO 的功能关联,这些目标蛋白的功能后来通过手动调节确定,用于评估参与预测因子的性能;迄今为止的 CAFA 挑战表明,PFP 仍然是一个开放的问题。以前的研究已经表明,复杂的计算问题,其中特征是高维的并且具有复杂/非线性关系,适合基于深度学习的技术。这些技术可以有效地从嘈杂的高维输入数据中学习与任务相关的表示。因此,深度学习已成功应用于计算机视觉、自然语言处理和生命科学等各个领域。生物分子的特征(例如,基因、蛋白质、RNA 等)应被提取并编码为定量/数值向量(即表示),以用于基于机器/深度学习的预测建模。给定生物分子的原始和高维输入特征,表示模型将该特征向量计算为该生物分子的简洁和正交表示。经过优化训练的监督预测系统可以有效地学习数据集中样本的特征,并使用这些表示作为输入来执行预测任务(例如,序列上的 DNA 结合区域、生化特性、亚细胞定位等)。蛋白质表示方法可以分为两大类;(1)经典表示(即模型驱动的方法),使用预定义的属性规则生成,例如基因/蛋白质之间的进化关系或氨基酸的物理化学性质,以及(2)数据驱动的表示,使用统计和机器学习算法(例如人工神经网络)构建,这些算法针对预定义任务进行训练,例如预测序列上的下一个氨基酸。之后,训练模型的输出——即表示特征向量——可以用于其他与蛋白质信息学相关的任务,例如功能预测。从这个意义上说,表示学习模型利用了知识从一个任务到另一个任务的转移。这个过程的广义形式被称为迁移学习,据报道它在时间和成本方面是一种高效的数据分析方法。因此,蛋白质表示学习模型最大限度地减少了对数据标记的需求。蛋白质表示学习是一个年轻但高度活跃的研究领域,主要受到自然语言处理 (NLP) 方法的启发。因此,蛋白质表示学习方法在文献中经常被称为蛋白质语言模型。之前的研究表明,各种蛋白质表示学习方法,尤其是那些结合了深度学习的方法,已经成功地提取了蛋白质的相关固有特征。参见:https://www.nature.com/articles/s42256-022-00457-9/tables/1尽管有研究评估学习的蛋白质表示模型,但需要进行全面的调查和基准测试,以便在学习蛋白质的多个方面(包括基于本体的功能定义、语义关系、家族和相互作用)的背景下系统地评估这些方法。在新的研究中,中东科技大学的研究人员对自 2015 年以来提出的可用蛋白质表示学习方法进行了全面调查,并通过详细的基准分析测量了这些方法捕获蛋白质功能特性的潜力。涵盖了经典和基于人工学习的方法,并深入了解了它们各自代表蛋白质的方法。研究人员根据它们的技术特征和应用对这些方法进行分类。为了评估每个表示模型在多大程度上捕获了功能信息的不同方面,该团队构建并应用了基于以下的基准:(1) 蛋白质之间的语义相似性推断,(2) 基于本体的 PFP,(3) 药物靶蛋白家族分类,(4) 蛋白质-蛋白质结合亲和力估计。

图示:研究的示意图。(来源:论文)此外,该团队还提供了相关的基准测试软件(Protein Representation Benchmark, PROBE),它允许人们轻松评估任何表示方法在该团队定义的四个基准测试任务中的性能。研究人员希望该工作能够,为希望将基于机器/深度学习的表示技术,应用于生物分子数据进行预测建模的研究人员提供信息。也希望这项研究能够激发新的想法,以开发新颖、复杂和强大的以数据为中心的方法来解决蛋白质科学中的开放问题。基于表示学习的方法在蛋白质功能分析中的表现通常优于经典方法在该团队所有的基准测试中,观察到学习表示(尤其是大型模型)在预测性能方面优于经典模型,证实了基于人工学习的数据驱动方法在表示生物分子的功能特性方面的优势。另一方面,在 PFP 预测基准的分子功能类别中,HMMER 是一种基于隐马尔可夫模型(HMM)的生物分子相似性检测和功能注释的经典方法,可以与基于深度学习的蛋白质表示方法竞争。该结果与先前的研究一致,即序列相似性与蛋白质的生化特性高度相关,以至于使用此特征的简单矢量表示几乎可以执行复杂的序列建模方法。鉴于这些结果,研究人员表示,将同源信息明确纳入表征学习模型的训练可能会导致考虑到预测性能的改进。这从基于深度学习的高性能蛋白质结构预测器(例如 RoseTTAFold 和 AlphaFold2)中也很明显,它们使用多个序列比对来显着丰富基于序列的输入。他们认为,在当前状态下,学习到的蛋白质表示对于其他原因也是必不可少的。模型设计和训练数据类型/来源是表征学习的关键因素蛋白质表征学习中最关键的因素之一是表征模型的设计。例如,在这里的基准测试中,包含了两种类型的 BERT 模型。TAPE-BERT-PFAM 接受了 3200 万个蛋白质结构域序列的训练。ProtBERT-BFD 训练有 21 亿个宏基因组序列片段;然而,这两者之间的性能差异是微不足道的。另一方面,使用相同 2.1B 数据集(例如 ProtT5-XL)训练的更复杂的模型在大多数基准测试中表现出更好的性能。因此,研究人员认为模型设计/架构是最重要的(与这些方法的设计/架构相关的信息在方法中给出,并在结果部分就预测性能进行了讨论)。关于训练数据源的另一个发现是,合并多种数据类型可能会在与功能相关的预测任务中带来更好的性能。例如,AAC 和 APAAC 都使用氨基酸组成;然而,APAAC 还在其表示模型中添加了物理化学特性,并且在语义相似性推断和 PFP 基准测试中表现得更好。同样,Mut2Vec 结合了突变配置文件、PPI 和文本数据,并取得了最佳性能;尤其是在语义相似性推理基准测试中。在蛋白质表征学习方法的构建和评估过程中应考虑潜在的数据泄漏数据泄漏可以定义为机器学习方法的训练和验证阶段之间的知识意外泄漏,导致性能测量过于乐观,是性能测试期间应考虑的关键问题。研究人员分析发现,某些表示模型在与这些模型预训练的任务生物学相关的任务中表现良好;尽管数据和实际任务彼此不同。蛋白质表征学习的现状和挑战蛋白质表示学习领域存在一些挑战。尽管大多数蛋白质表示学习模型(迄今为止提出的)都是源自 NLP 模型(基于 LSTM/transformer 的深度学习模型),但建模语言和蛋白质的问题之间存在结构差异。据估计,一个以美国为母语的成年英语使用者,平均使用 46,200 个词条和多词表达;然而,蛋白质中只有 20 种不同的氨基酸,它们被表示模型以类似于语言的引理的方式处理。这些 NLP 模型为每个单词计算一个表示向量。类似地,当这种方法应用于蛋白质序列数据时,会为每个氨基酸计算一个表示向量。这些向量被汇集起来,为每个句子/文档和蛋白质创建固定大小的向量,分别用于 NLP 和蛋白质信息学任务。因此,与 NLP 相比,蛋白质表示中的少量构建块(即 20 个氨基酸)可能为较小的模型在与蛋白质表示学习领域中的较大模型竞争时带来优势。因此,鼓励对蛋白质序列特异性学习模型进行更多研究。

图示:蛋白质语义相似性推理基准结果。(来源:论文)模型的可解释性对于理解模型为何如此行事至关重要。在可解释表示中,所有特征都以隔离形式编码,这意味着向量上每个位置对应的特征是已知的;然而,该研究中研究的大多数学习蛋白质表示是不可解释/可解释的。例如,蛋白质中 TIM 桶结构的存在可能在其表示向量的第五位编码,而分子量信息可能在第三和第四位之间共享。一般来说,在数据科学领域,解缠结研究试图将样本的真实属性与输出向量的各个位置联系起来。蛋白质表征的解开是一个新课题,迄今为止只有少数表征模型开发人员探讨了这个问题。因此,尚不存在系统方法,并且需要新的框架来标准化评估蛋白质表示模型的可解释性。迄今为止提出的大多数蛋白质表示模型仅使用一种类型的数据(例如蛋白质序列)进行训练。然而,蛋白质知识与多种类型的生物信息相关,例如 PPI、翻译后修饰、基因/蛋白质(共)表达等;只有少数可用的蛋白质表示模型使用了多种类型的数据。在该团队的基准研究时所涉及的方法中,Mut2Vec 就是一个这样的例子,它结合了 PPI、突变和生物医学文本,并且比 GO BP 和基于 CC 的 PFP 中的许多仅基于序列的表示产生了更准确的结果。研究人员建议整合其他类型的蛋白质相关数据,尤其是进化关系,可能会进一步提高预测任务的准确性。MSA-Transformer 和无向图模型(例如 DeepSequence)通过深度学习利用同源信息。DeepSequence 使用 MSA 的后验分布计算潜在因子,而 MSA-Transformer 使用基于行和列的注意力来结合 MSA 和蛋白质语言模型。尽管 MSA-Transformer 在基准测试中表现出平均性能,但在之前的文献中,发现它在二级结构和接触预测任务上是成功的,这表明 MSA-Transformer 具有捕捉进化关系的能力。与此相关的是,文献中明确要求能够从广义的角度有效地表示蛋白质的整体蛋白质载体,用于各种不同的蛋白质信息学相关目的。研究人员认为,可以通过连接多个先前使用不同类型的生物数据独立构建的表示向量来创建这些整体表示,并使用这些向量的集成版本训练新模型以用于高级监督任务,例如预测生物过程和/或复杂的结构特征。构建这些整体表示的另一种方法是通过图表示学习直接学习整合多种蛋白质关系(例如,其他蛋白质、配体、疾病、表型、功能、途径等)的异构图。

图示:基于本体的蛋白质功能预测基准结果。(来源:论文)蛋白质表示学习方法可用于设计新蛋白质蛋白质设计是生物技术的主要挑战之一。合理的蛋白质设计涉及评估许多不同替代序列/结构的活性和功能,以为实验验证提供最有希望的候选者,这可以看作是一个优化问题。为此目的要探索的序列空间是巨大的。例如,人类蛋白质的平均长度约为 350 个氨基酸,其中存在 20^350 种不同的组合,尽管其中大多数是非功能性序列。在过去的几十年中,计算方法已被用于蛋白质设计,并且这些方法已经产生了有希望的结果,特别是在酶设计、蛋白质折叠和组装以及蛋白质表面设计方面。因此已经开发出高效的抗体和生物传感器。其中一些方法使用量子力学计算、分子动力学和统计力学,每种方法的计算成本都非常高,并且需要专业知识。类似的缺点也表现在主流的蛋白质设计软件,如 Rosetta。最近的研究表明,基于人工学习的生成建模可用于从头蛋白质设计。在机器学习领域,生成建模与判别建模相反,是一种生成合成样本的方法,这些样本服从从真实样本中学习到的概率分布。这是通过有效地学习训练数据集中样本的表示来实现的。

图示:药物靶蛋白家族分类基准结果。(来源:论文)深度学习最近已成为生成模型架构的关键方法,并已应用于包括蛋白质/肽设计在内的各个领域。例如,Madani 团队使用蛋白质语言模型从头开始设计属于不同蛋白质家族的新功能蛋白质,并通过湿实验室实验验证了他们的设计。这些研究表明,表示学习对于蛋白质和配体(药物)设计中的新应用至关重要。

图示:蛋白质-蛋白质结合亲和力估计基准结果。(来源:论文)研究人员相信蛋白质表示学习方法将在不久的将来对蛋白质科学的各个领域产生影响,并在现实世界中应用,这要归功于它们在输入级别集成异构蛋白质数据(即理化性质/属性、功能注释等)的灵活性,以及它们有效提取复杂潜在特征的能力。论文链接:https://www.nature.com/articles/s42256-022-00457-9

__call__方法和可调用对象

在看神经网络代码的时候,类定义中总是会出现__call__方法,它是Python类体中可以定义的一个特殊方法,定义了该方法的对象称为可调用对象,即该对象可以像函数一样被调用。

定义一个可以求解自由落体下降高度的对象

类的实例:如果类定义了 __call__ 方法,那么它的实例可以作为函数调用。每执行一次实例,__call__函数就执行一遍:

可以理解为 实例() ==实例.__call__()

class GDistance:
    def __init__(self, g):
        self.g = g
    def __call__(self, t):
        # 自由落体下降距离 s=g*t^2
        return (self.g * t ** 2)/2

调用对象

e_gdist = GDistance(9.8)
for t in range(11):
    print("%d 秒 下降%.2f 米" %(t, e_gdist(t)))

输出结果

0 秒 下降0.00 米
1 秒 下降4.90 米
2 秒 下降19.60 米
3 秒 下降44.10 米
4 秒 下降78.40 米
5 秒 下降122.50 米
6 秒 下降176.40 米
7 秒 下降240.10 米
8 秒 下降313.60 米
9 秒 下降396.90 米
10 秒 下降490.00 米

总之:如果一个类中定义了 __call__ 方法,那么该类的实例可以作为函数调用,并执行__call__方法所定义的内容。

  1. class CLanguage:
  2. # 定义__call__方法
  3. def __call__(self,name,add):
  4. print(“调用__call__()方法”,name,add)
  5. clangs = CLanguage()
  6. clangs(“C语言中文网”,”http://c.biancheng.net”)

程序执行结果为:

调用__call__()方法 C语言中文网 http://c.biancheng.net

可以看到,通过在 CLanguage 类中实现 __call__() 方法,使的 clangs 实例对象变为了可调用对象。

Python 中,凡是可以将 () 直接应用到自身并执行,都称为可调用对象。可调用对象包括自定义的函数、Python 内置函数以及本节所讲的类实例对象。

对于可调用对象,实际上“名称()”可以理解为是“名称.__call__()”的简写。仍以上面程序中定义的 clangs 实例对象为例,其最后一行代码还可以改写为如下形式:

clangs.__call__("C语言中文网","http://c.biancheng.net")