作者归档:runyu

Python

pyside6-deploy

This tool is intended to help in-experienced developers with freezing and compiling tools to get an executable from their PySide project. We include a simple wrapper for Nuitka, so you can just run pyside6-deploy your_file.py to get it done. 

此工具旨在帮助经验丰富的开发人员使用冻结和编译工具,从PySide项目中获取可执行文件。我们为Nuitka提供了一个简单的包装器,因此您只需运行pyside6-deploy your_file.py来完成它。

类属性和实例属性

class test:
    a = 1;
    def __init__(self):
        print(self.a)
        self.a = 2
        print(self.a)
        print(test.a)
        test.a = 2
        print(test.a)
        
new = test()
print(new.a)
输出结果:
1
2
1
2

程序员注意事项: 在类定义内定义的变量是类属性;它们将被类实例所共享。 实例属性可通过 self.name = value 在方法中设定。 类和实例属性均可通过 “self.name” 表示法来访问,当通过此方式访问时实例属性会隐藏同名的类属性。 类属性可被用作实例属性的默认值,但在此场景下使用可变值可能导致未预期的结果。 可以使用 描述器 来创建具有不同实现细节的实例变量。来源

全局解释器锁(GIL)

全局解释器锁(GIL)是 Python 解释器的一个重要组成部分,它是一个互斥锁,用于控制在任何时刻只有一个线程可以执行 Python 解释器中的字节码。这个锁可以确保在多线程环境下,Python 程序能够正确地执行,避免了并发访问共享数据时的竞态条件和数据不一致性问题。

GIL 的作用是在解释器级别上限制了线程的并发性,因为在任何时刻只能有一个线程持有 GIL,获得 GIL 的线程可以执行 Python 字节码,而其他线程则被挂起。这样做可以避免多个线程同时访问共享数据时的竞态条件和数据不一致性问题,因为只有一个线程可以访问共享数据,而其他线程必须等待。

GIL 的存在在某种程度上限制了 Python 的多线程并发性能,因为在任何时刻只有一个线程可以执行 Python 代码,而其他线程被挂起。如果程序中的多线程任务是 CPU 密集型的,那么这个限制可能会对程序性能产生重大影响。然而,在 I/O 密集型任务中,由于 I/O 操作通常会导致线程挂起,因此 GIL 的限制通常不会成为程序性能的瓶颈。

需要注意的是,GIL 是特定于 CPython 解释器的实现细节,其他 Python 实现(如 Jython、IronPython 和 PyPy)可能没有 GIL 或使用不同的锁实现。同时,对于需要高度并发性的应用程序,可以使用其他并发编程技术(如多进程、协程等)来避免 GIL 限制。

Python 的协程

协程(用户态线程)

由于线程分用户线程和内核线程。内核线程再调用的时候可以去不同的核心去操作。所以多线程是可以利用到多核的。

Python 的协程(coroutine)是一种轻量级的并发编程技术,可以在单个线程中实现并发执行多个任务的效果,从而提高程序的性能和响应能力。下面是一个简单的协程示例:

import asyncio

async def greet(name):
    print(f"Hello, {name}!")
    await asyncio.sleep(1)
    print(f"Goodbye, {name}!")

async def main():
    await asyncio.gather(
        greet("Alice"),
        greet("Bob"),
        greet("Charlie")
    )

asyncio.run(main())

在这个例子中,greet 函数是一个协程,它使用 async def 声明,这表示它是一个异步函数。该函数包含了两个打印语句和一个 await asyncio.sleep(1) 语句。await 表示当前的协程需要等待一个异步操作完成,而这里的异步操作是 asyncio.sleep(1),它会让协程挂起一秒钟,然后恢复执行。

main 函数中,使用 asyncio.gather 函数来并发执行三个 greet 协程。这个函数会等待所有协程执行完毕,然后返回它们的结果。

运行这个示例后,我们会看到类似如下的输出:

Hello, Bob!
Hello, Charlie!
Goodbye, Alice!
Goodbye, Bob!
Goodbye, Charlie!

可以看到,在这个示例中,三个 greet 协程被并发执行,每个协程执行到 await asyncio.sleep(1) 时会挂起,然后切换到其他协程继续执行。这样可以实现在单个线程中同时执行多个任务的效果,从而提高程序的性能和响应能力。

需要注意的是,在使用协程时需要注意避免阻塞操作,因为协程是在单个线程中并发执行的,如果一个协程阻塞了,那么其他协程也会被阻塞。通常可以使用异步 I/O 或者线程池等技术来避免阻塞操作,从而提高程序的性能和并发能力。

Python协程(coroutine)是一种更高级的控制流结构,可以看作是生成器的扩展。与生成器类似,协程也是通过关键字 yield 来暂停和恢复执行的,但协程可以在暂停的地方重新启动执行,甚至可以从外部发送数据进入协程。要理解Python协程的底层原理,以下几个关键点是必须掌握的:

  1. 生成器
    • 生成器是迭代器的一种特殊形式,通过 yield 关键字生成值。
    • 调用 yield 的时候,生成器会暂停执行,并将值返回给调用方。
    • 当生成器函数再次被调用时,会从上次 yield 的位置继续执行。
  2. 生成器对象
    • 当一个生成器函数被调用时,它返回一个生成器对象,而不是立即开始执行函数体。
    • 生成器对象实现了迭代器协议 (__iter____next__ 方法)。
  3. 协程基础
    • 协程也是通过 yield 实现的,但更进一步的是,协程允许通过 yield 向其内部发送值。
    • 协程通过 yield 表达式接收外部发送的数据,这使得协程不仅能生成值,还能接收值并控制执行流程。
  4. yield 表达式
    • yield 可以作为表达式来使用,例如 value = (yield some_value),这样 yield 不仅返回 some_value,而且可以通过 send 方法接收外部传入的 value
  5. 协程的状态控制
    • 协程的状态由其生成器对象的生命周期管理。当调用 next()send() 方法时,协程会从上次暂停的地方继续执行。
    • 当协程运行结束时,会引发 StopIteration 异常。
  6. asyncawait
    • async def 声明一个协程函数,await 关键字用于暂停协程的执行,等待一个耗时操作完成。
    • 底层仍然是基于生成器的工作机制,但有更好的语法支持和语义约束。

下面是一个简单的例子来演示协程的工作机制:

python

复制代码

def simple_coroutine(): print('Coroutine started') x = yield print('Received:', x) # 创建生成器对象 coro = simple_coroutine() # 启动协程 next(coro) # 输出: Coroutine started # 向协程发送数据 coro.send(42) # 输出: Received: 42

在这个例子中,simple_coroutine 是一个简单的协程,使用 yield 来暂停和恢复执行。在 coro.send(42) 调用之后,值 42 被发送到协程内部,并赋值给变量 x

底层实现原理

Python的协程底层实现主要依赖于生成器的状态管理机制。每次调用 yield 时,生成器对象的状态会保存到生成器帧中。生成器帧包含当前执行的位置(指令计数器)、局部变量和堆栈状态。通过调用 next()send() 方法,生成器帧会被恢复,并继续执行直到遇到下一个 yield 或函数返回。

在Cpython中,协程的执行由_PyGen_Send函数控制,该函数负责恢复生成器帧的执行,并处理yield表达式的值传递。协程的高级特性如asyncawait则通过事件循环(如asyncio)进行管理,提供异步I/O操作的支持。

总结起来,Python协程的底层原理主要基于生成器的状态管理机制,通过yield实现暂停和恢复执行,并通过事件循环实现异步操作。这使得协程在处理异步编程任务时非常高效和灵活。

生成器

Python的生成器是一种特殊的迭代器,可以用来生成一系列的值,而不需要一次性将所有值存储在内存中。生成器可以通过 yield 关键字来定义,在函数执行时,当遇到 yield 语句时,会将当前函数的状态保存下来,并返回一个生成器对象。每次从生成器中调用 __next__() 方法时,函数会从上次暂停的地方继续执行,直到遇到下一个 yield 语句或函数结束。

生成器的好处在于,它可以节省内存空间,并且在处理大量数据时,可以实现按需生成数据,提高程序的效率。下面是一个简单的示例代码,演示了如何使用生成器来生成斐波那契数列:

def fibonacci():
    a, b = 0, 1
    while True:
        yield a
        a, b = b, a + b

fib = fibonacci()
for i in range(10):
    print(next(fib))

上面的代码定义了一个名为 fibonacci() 的生成器函数,使用 yield 语句生成斐波那契数列。在主程序中,创建了一个生成器对象 fib,并通过 next() 方法调用了10次,每次调用都会输出一个斐波那契数列的元素。

生成器是一种非常强大的工具,可以用来处理大量的数据,提高程序的效率,也可以用来实现协程,完成异步编程等高级应用。

在生成器函数中,通常会使用 while True 循环来不断生成数据。这是因为生成器是一种惰性求值的迭代器,它只在需要时才会生成数据,并且可以不限次数地生成数据。因此,当我们在使用生成器时,通常会通过 next() 方法不断从生成器中取出数据,直到生成器结束。

在生成器函数中使用 while True 循环,可以保证生成器可以不限次数地生成数据,直到程序主动终止或者生成器函数内部返回一个 StopIteration 异常来结束生成器。在 while True 循环中,通常还会使用 yield 语句来返回生成器的值,因此每次调用 next() 方法时,都会从上次暂停的位置继续执行生成器函数,并生成下一个值,直到结束为止。

惰性求值(Lazy evaluation)是一种编程语言的求值策略,它的特点是在程序需要使用数据时才进行求值。与之相对的是及早求值(Eager evaluation),它的特点是在程序执行时尽可能早地对表达式进行求值。

在惰性求值的策略中,表达式的求值被推迟到程序需要使用它的结果时。这种策略的优势在于可以避免一些不必要的计算和存储,从而提高程序的效率和性能。另外,惰性求值还可以让程序具有更好的组合性和灵活性,因为程序可以按需组合各种操作,并且不必事先计算所有的结果。

在 Python 中,生成器是一种惰性求值的迭代器,它只在需要时才会生成数据,并且可以不限次数地生成数据。通过使用生成器,我们可以避免一次性生成大量的数据导致内存溢出等问题,同时也可以使程序具有更好的组合性和灵活性。除了生成器,Python 中还有一些惰性求值的工具和函数,例如 map()filter()zip() 等。

map()filter()zip()

这三个函数是 Python 内置的用于操作可迭代对象的函数,它们都属于惰性求值的策略。

  1. map()
numbers = [1, 2, 3, 4, 5]
squares = map(lambda x: x**2, numbers)
print(list(squares))  # [1, 4, 9, 16, 25]

map() 函数接受一个函数和一个可迭代对象,它会对可迭代对象中的每个元素应用函数,并返回一个新的可迭代对象。例如,我们可以使用 map() 函数对列表中的所有元素求平方:

  1. filter()

filter() 函数接受一个函数和一个可迭代对象,它会对可迭代对象中的每个元素应用函数,返回所有使函数返回 True 的元素组成的新的可迭代对象。例如,我们可以使用 filter() 函数过滤掉列表中的奇数:

numbers = [1, 2, 3, 4, 5]
even_numbers = filter(lambda x: x % 2 == 0, numbers)
print(list(even_numbers))  # [2, 4]
  1. zip()

zip() 函数接受多个可迭代对象,它会返回一个新的可迭代对象,其中每个元素是输入的可迭代对象中对应位置的元素组成的元组。例如,我们可以使用 zip() 函数将两个列表按位置组合:

names = ["Alice", "Bob", "Charlie"]
ages = [20, 30, 40]
data = zip(names, ages)
print(list(data))  # [("Alice", 20), ("Bob", 30), ("Charlie", 40)]

这三个函数都是非常常用的 Python 内置函数,它们可以让我们在不显式遍历可迭代对象的情况下,对其进行操作和处理,从而提高程序的效率和可读性。同时,它们也支持惰性求值的特性,可以处理非常大的数据集而不会造成内存溢出等问题。

1、*args保存多余变量,保存方式为元组。
2、**args保存带有变量名的多余变量,保存方式为字典。

迭代器在访问元素时不会复制可迭代对象,它只是在内部维护一个指针(或索引),用以记录当前访问的位置。

在 Python 中,要构造一个可迭代对象,需要实现 __iter__() 方法。可迭代对象可以是任何实现了 __iter__() 方法并返回一个迭代器的对象。

迭代器对象还需要实现 __next__() 方法,以便可以逐一访问元素。

class MyRange:
    def __init__(self, start, end):
        self.start = start
        self.end = end

    def __iter__(self):
        return MyRangeIterator(self.start, self.end)

class MyRangeIterator:
    def __init__(self, start, end):
        self.current = start
        self.end = end

    def __iter__(self):
        return self

    def __next__(self):
        if self.current >= self.end:
            raise StopIteration
        else:
            current = self.current
            self.current += 1
            return current

# 使用自定义的可迭代对象
my_range = MyRange(1, 5)

for num in my_range:
    print(num)  # 输出: 1 2 3 4

生成器是一种特殊的迭代器ーー优雅的迭代器。

推导式 Comprehension

Comprehension是一种简洁的语法结构,用于生成新的序列(如列表、集合、字典等)。Python支持四种主要的comprehension:列表推导式(List Comprehension)、集合推导式(Set Comprehension)、字典推导式(Dictionary Comprehension)和生成器表达式(Generator Expression)。

[表达式 for 变量 in 可迭代对象 if 条件]

字符串方法

内置类型 — Python 3.12.4 文档

Fredrik Lundh 曾经建议以下一组规则来重构 lambda 的使用:

  1. 写一个 lambda 函数。
  2. 写一句注释来说明这个 lambda 究竟干了什么。
  3. 研究一会这个注释,然后想出一个抓住注释本质的名字。
  4. 用这个名字,把这个 lambda 改写成 def 语句。
  5. 把注释去掉。

“超类”是指一个类的父类或基类。一个类可以从一个或多个超类继承属性和方法。继承是面向对象编程中的一个重要概念,它允许你创建一个新类,该类可以重用一个或多个已存在类的属性和方法。

子类化是指创建一个新的类,该类从一个或多个已存在的类中继承属性和方法。新创建的类称为子类,而被继承的类称为父类或超类。子类可以继承父类的特性,并且可以通过添加新的属性和方法来扩展或修改其行为。

Python 切片语法是一种便捷的方法,用于从序列(如列表、元组、字符串等)中提取部分元素。Python 的切片操作非常灵活,基本语法如下:

arduino

Copy code

序列[start:stop:step]

这里,startstopstep 分别表示切片的起始位置、终止位置和步长。下面是具体的介绍:

  1. start: 切片的开始索引,序列从这里开始提取。如果不指定,默认从序列的开始处提取。
  2. stop: 切片的结束索引,序列在这里结束提取,但不包括此索引指向的元素。如果不指定,默认提取到序列的末尾。
  3. step: 步长决定了从startstop间提取元素的间隔。如果不指定,默认步长为1,即连续提取。步长可以是负数,这时会逆向提取元素。

示例

  • 基本切片:pythonCopy codelst = [0, 1, 2, 3, 4, 5] print(lst[1:4]) # 输出: [1, 2, 3]
  • 省略开始或结束索引:pythonCopy codeprint(lst[:3]) # 输出: [0, 1, 2] print(lst[3:]) # 输出: [3, 4, 5]
  • 负数索引:pythonCopy codeprint(lst[-4:-1]) # 输出: [2, 3, 4]
  • 步长使用:pythonCopy codeprint(lst[0:5:2]) # 输出: [0, 2, 4] print(lst[::-1]) # 输出: [5, 4, 3, 2, 1, 0], 逆序列表

注意点

  • 如果startstop指定的索引超出了序列的范围,Python 会自动将其调整到序列的边界。
  • 切片操作返回一个新的序列,不影响原始序列。

切片是处理数据时非常有用的工具,能够简洁地表达复杂的序列操作。在数据处理、机器学习等领域中非常常见。

method — 方法

在类内部定义的函数。如果作为该类的实例的一个属性来调用,方法将会获取实例对象作为其第一个 argument (通常命名为 self)。参见 function 和 nested scope

descriptor — 描述器

任何定义了 __get__()__set__() 或 __delete__() 方法的对象。 当一个类属性为描述器时,它的特殊绑定行为就会在属性查找时被触发。 通常情况下,使用 a.b 来获取、设置或删除一个属性时会在 a 类的字典中查找名称为 b 的对象,但如果 b 是一个描述器,则会调用对应的描述器方法。 理解描述器的概念是更深层次理解 Python 的关键,因为这是许多重要特性的基础,包括函数、方法、特征属性、类方法、静态方法以及对超类的引用等等。

CycleGAN

real_A:表示输入的真实的A图片
real_B:表示输入的真实的B图片
fake_B:表示真实图片A生成的假冒B风格图片
fake_A:表示真实图片B生成的假冒A风格图片
rec_A:表示fake_B再生成回A风格图片
rec_B:表示fake_A再生成回B风格图片
idt_B:表示真实图片A生成的A风格图片
idt_A:表示真实图片B生成的B风格图片

其中fake_B一般就是想要生成的图片

netG_A:生成器A,用于生成B风格的图片
netG_B:生成器B,用于生成A风格的图片
————————————————

C#

成员

类型的成员包括所有方法、字段、常量、属性和事件。 C# 没有全局变量或方法,这一点其他某些语言不同。 即使是编程的入口点(Main 方法),也必须在类或结构中声明(使用顶级语句时,隐式声明)。

下面列出了所有可以在类、结构或记录中声明的各种成员。

  • 字段Fields
  • 常量
  • 属性
  • 方法
  • 构造函数
  • 事件
  • 终结器
  • 索引器
  • 运算符
  • 嵌套类型

JIT (Just-In-Time – 实时编译) 和 AOT (Ahead-Of-Time – 预先编译)

C# 10 中可使用文件范围的命名空间声明。

namespace SampleNamespace;

what?

歃血为盟 [ shà xuè wéi méng ]

睚眦必报 [ yá zì bì bào ]

后顾之虞 hòu gù zhī yú

沆瀣一气 [ hàng xiè yī qì ]

已臻化境 yǐ zhēnhuàjìng

风光旖旎 fēng guāng yǐ nǐ

机器学习笔记

计算图是用图论语言表示数学函数的一种方式。计算图被定义为有向图,其中节点对应于数学运算。节点由边连接,图中的一切要么是节点,要么是边。

PyTorch提供的autograd包能够根据输入和前向传播过程自动构建计算图,并执行反向传播。

零阶张量:标量 一阶张量:向量 Tensors of rank 1

线性回归

矩阵求导

范数 ∥x∥

前馈神经网络具有很强的拟合能力 (通用近似定理)

正则化(Regularization)是一种在机器学习和统计学中广泛使用的技术,主要用于防止过拟合(Overfitting)。过拟合是指模型在训练数据上表现得很好,但在未见过的测试数据上表现较差。过拟合的原因通常是模型过于复杂,以至于它开始学习训练数据中的噪声,而非数据中真实的关系。

正则化通过向模型的损失函数(Loss function)添加一个额外的正则项(Regularization term)来实现。这个正则项通常与模型的权重(Weights)有关,可以帮助限制模型的复杂度。常见的正则化方法有L1正则化和L2正则化。

L1正则化将权重的绝对值之和添加到损失函数中,这会导致模型中的许多权重变为零,从而产生稀疏模型。L2正则化则将权重平方和添加到损失函数中,这会使得模型的权重更加接近于零,但不会产生稀疏解。

通过使用正则化,我们可以在模型复杂度和泛化能力之间找到一个平衡点,从而提高模型在未见过的数据上的性能。

归一化(Normalization)和正则化(Regularization)是两种不同的概念,它们在机器学习和统计学中都有重要的应用。以下是它们之间的主要区别:

  1. 目的:

归一化的目的是调整特征值的范围,以消除不同尺度和量纲对模型的影响。通过将特征值缩放到相同的范围(例如 [0, 1] 或者均值为 0,方差为 1 的正态分布),可以加快模型的收敛速度,提高模型的性能。

正则化的目的是防止模型过拟合,提高模型的泛化能力。通过在损失函数中添加正则项,正则化方法限制了模型权重的大小,从而降低了模型的复杂度。

  1. 方法:

归一化是一种预处理技术,通常在模型训练之前对数据进行处理。常见的归一化方法有最小-最大缩放(Min-Max Scaling)、Z分数标准化(Z-score Normalization)等。

正则化则是模型训练过程中使用的一种方法,通过向损失函数添加正则项来实现。常见的正则化方法有 L1 正则化和 L2 正则化。

总结一下,归一化主要关注数据预处理,使得特征值在相同的尺度上,以便于模型训练;而正则化关注模型的复杂度,通过限制模型权重的大小来防止过拟合。两者都对提高模型性能有积极作用,但它们的关注点和方法有所不同。

在机器学习模型中,尤其是神经网络模型中,权重(Weights)是指模型内部的参数,它们决定了模型如何根据输入特征来进行预测。模型权重的大小通常是指这些参数的数值大小。

以线性回归模型为例,模型的目标是学习一组权重 w 和偏置项 b,使得模型可以尽可能准确地预测输出 y。这个模型可以表示为:

y = w1 * x1 + w2 * x2 + … + wn * xn + b

其中,w1、w2、…、wn 是权重,x1、x2、…、xn 是输入特征,y 是预测输出,b 是偏置项。在这个例子中,权重的大小是指 w1、w2、…、wn 的数值大小。

在训练过程中,模型会调整权重和偏置项的值,以便在训练数据上获得最小的损失。权重的大小会影响模型的复杂度。权重较大可能导致模型过拟合,因为它可能会过度强调某些特征,从而在训练数据上表现得很好,但在新数据上泛化能力较差。而权重较小则会降低模型的复杂度,有助于防止过拟合,但是过小的权重可能导致模型欠拟合,即在训练数据和新数据上都表现不佳。

正则化方法(如 L1 和 L2 正则化)通过向损失函数添加一个与权重大小相关的正则项来限制权重的大小,从而在模型复杂度和泛化能力之间达到一个平衡。

梯度消失和模式崩溃是深度学习中常见的两种训练问题,尤其是在训练生成对抗网络(GAN)时。这两个问题的具体描述如下:

  1. 梯度消失(Gradient Vanishing):梯度消失是指在训练深度神经网络时,网络中较低层的梯度值变得非常小,接近于零。这会导致权重更新变得非常缓慢,从而使网络难以学习到这些层的有用特征。梯度消失通常与激活函数(如 Sigmoid 和 Tanh)和深度网络结构有关。为了解决梯度消失问题,可以采用以下策略:
    • 使用 ReLU(Rectified Linear Unit)或其他抗梯度消失的激活函数。
    • 使用批量归一化(Batch Normalization)来减少梯度消失的影响。
    • 使用残差连接(Residual Connection)以便梯度更容易地通过网络传播。
    • 调整网络架构,减小网络深度。
  2. 模式崩溃(Mode Collapse):模式崩溃是指在训练生成对抗网络(GAN)时,生成器产生的输出变得单一和相似,丧失了多样性。这通常是因为生成器和判别器之间的训练不平衡导致的。当生成器学会生成一种“简单”的输出以欺骗判别器时,它可能会陷入这种局部最优解。为了解决模式崩溃问题,可以采用以下策略:
    • 为损失函数添加多样性惩罚项,以鼓励生成器产生更多样化的输出。
    • 使用特征匹配(Feature Matching)或最小类间距(Minibatch Discrimination)等技术,以便判别器关注更丰富的特征。
    • 调整训练策略,例如使用 WGAN(Wasserstein GAN)或 WGAN-GP(Wasserstein GAN with Gradient Penalty)等改进的 GAN 训练方法。
    • 更平衡地训练生成器和判别器,例如使用 TTUR(Two Time-Scale Update Rule)。

这两个问题可能会影响模型的性能和训练速度,因此在训练深度学习模型和 GAN 时需要特别关注。解决这些问题通常需要调整网络架构、激活函数和训练策略等方面的设置。

为什么卷积网络可以接受不同尺寸的数据输入

卷积神经网络(Convolutional Neural Network,CNN)可以接受不同尺寸的数据输入,这是因为CNN中的卷积层和池化层等操作都是局部操作,不关心输入数据的整体尺寸大小,而是关注数据的局部结构和特征。

具体来说,卷积层通过滑动卷积核在输入数据上进行卷积操作,卷积核的大小是固定的,而输入数据的大小可以是任意的。卷积核在输入数据上滑动时,只关注当前滑动位置的局部区域,并进行卷积运算,因此输入数据的尺寸可以是任意的。

同样,池化层也是通过对局部区域进行池化操作,从而对数据进行下采样,进一步降低数据的尺寸大小。因此,池化层也可以接受不同尺寸的数据输入。

此外,卷积神经网络还可以通过使用全连接层等操作将输入数据的尺寸统一调整到相同的大小,以便于网络的后续处理。因此,卷积神经网络可以处理不同尺寸的输入数据,具有较强的灵活性和适应性。

卷积 计算公式

W:输入特征图的宽,H:输入特征图的高

K:kernel size卷积核宽和高,P:padding(特征图需要填充的0的个数),S:stride步长

width_out:卷积后输出特征图的宽,height_out:卷积后输出特征图的高

普通卷积
计算公式:
width_out = (W – K + 2 * P)/ S + 1(向下取整)

height_out = (H – K + 2 * P) / S + 1(向下取整)

池化
计算公式:
width_out = (W – K)/ S + 1(向下取整)

height_out = (H – K) / S + 1(向下取整)

上采样UpSampling2D
上采样相当于放大多少倍,size=倍数

计算公式:
width_out = W * size

height_out = H * size

转置卷积
转置卷积俗称反卷积,是上采样方式中的一种,转置卷积用来增大特征图的分辨率。

计算公式:
width_out = (W – 1)* S – 2 * P + K

height_out = (H – 1)* S – 2 * P + K

nn.Upsamplenn.ConvTranspose2d都可以实现图像的上采样操作,但它们的具体实现方式有所不同。

nn.Upsample是一种常用的上采样方式,它通过对输入特征图进行插值操作来实现图像的放大。具体来说,nn.Upsample通过双线性插值等方式,对输入特征图中的每个像素进行放大,并生成相应大小的输出特征图。虽然nn.Upsample能够实现图像的上采样,但由于其采用了插值等方式,因此可能会产生一定程度的平滑或失真。

nn.ConvTranspose2d则采用卷积操作来实现图像的上采样。具体来说,nn.ConvTranspose2d会对输入特征图进行补零操作,并对补零后的特征图进行卷积操作。这样可以在保证特征图尺寸不变的同时,通过卷积操作实现图像的放大。与nn.Upsample相比,nn.ConvTranspose2d更加灵活,并且可以通过调整卷积核大小、步长、填充等参数来控制图像的清晰度和质量。

需要注意的是,nn.ConvTranspose2d可能会产生一些棋盘格等不良影响,而nn.Upsample不会产生这些问题。因此,实际应用中应该根据具体情况选择合适的上采样方式。

CycleGAN的原始生成器采用的是转置卷积,会产生棋盘格

消融实验

消融实验(ablation study)是指通过逐步削减模型中的某些模块或组件,来验证这些模块或组件对模型性能的贡献。它通常被用于探究模型中的某些设计选择的有效性和必要性,或者对某些假设进行验证。

具体来说,消融实验通常分为以下几个步骤:

  1. 定义基准模型:定义一个基准模型作为对照组,它是最基本的模型,包含所有的设计组件。基准模型通常具有较高的性能。
  2. 削减模型组件:逐步削减模型中的某些组件,比如某些层、某些特征、某些连接方式等,得到一系列消融模型。
  3. 测试消融模型:对所有的消融模型和基准模型进行测试,比较它们的性能差异。可以采用各种指标进行评估,比如准确率、F1-score等。
  4. 分析结果:根据测试结果,分析不同模型之间的性能差异。可以发现哪些模型组件对模型性能有重要的贡献,哪些模型组件对模型性能没有显著的影响。

通过消融实验,我们可以深入了解模型的性能和设计选择,从而为进一步的模型优化和改进提供指导。

卷积网络参数量计算
参数量 = (输入通道数 × 卷积核尺寸 × 卷积核尺寸 + 1) × 卷积核个数(输出通道数)

摘记

一个人的命运啊,当然要靠自我奋斗,但是也要考虑到历史的行程

Imagine life as a game in which you are juggling some five balls in the air. You name them — work, family, health, friends and spirit and you’re keeping all of these in the air.You will soon understand that work is a rubber ball.If you drop it, it will bounce back. But the other four balls — family, health, friends, and spirit — are made of glass. If you drop one of these, they will be irrevocably scuffed, marked, nicked, damaged, or even shattered. They will never be the same. You must understand that and strive for balance in your life.

Build your own things

神经网络

直观的理解:
Batch Size定义:一次训练所选取的样本数。
Batch Size的大小影响模型的优化程度和速度。同时其直接影响到GPU内存的使用情况,假如你GPU内存不大,该数值最好设置小一点。

为什么要提出Batch Size?
在没有使用Batch Size之前,这意味着网络在训练时,是一次把所有的数据(整个数据库)输入网络中,然后计算它们的梯度进行反向传播,由于在计算梯度时使用了整个数据库,所以计算得到的梯度方向更为准确。但在这情况下,计算得到不同梯度值差别巨大,难以使用一个全局的学习率,所以这时一般使用Rprop这种基于梯度符号的训练算法,单独进行梯度更新。
在小样本数的数据库中,不使用Batch Size是可行的,而且效果也很好。但是一旦是大型的数据库,一次性把所有数据输进网络,肯定会引起内存的爆炸。所以就提出Batch Size的概念。

Batch Size设置合适时的优点:
1、通过并行化提高内存的利用率。就是尽量让你的GPU满载运行,提高训练速度。
2、单个epoch的迭代次数减少了,参数的调整也慢了,假如要达到相同的识别精度,需要更多的epoch。
3、适当Batch Size使得梯度下降方向更加准确。

Batch Size从小到大的变化对网络影响
1、没有Batch Size,梯度准确,只适用于小样本数据库
2、Batch Size=1,梯度变来变去,非常不准确,网络很难收敛。
3、Batch Size增大,梯度变准确,
4、Batch Size增大,梯度已经非常准确,再增加Batch Size也没有用

注意:Batch Size增大了,要到达相同的准确度,必须要增大epoch。

Pytorch

class NeuralNetwork(nn.Module):
    def __init__(self):
        super(NeuralNetwork, self).__init__()
        self.flatten = nn.Flatten()
        self.linear_relu_stack = nn.Sequential(
                (28*28, 512),
            nn.ReLU(),
            nn.Linear(512, 512),
            nn.ReLU(),
            nn.Linear(512, 10),
            nn.ReLU()
        )

    def forward(self, x):
        x = self.flatten(x)
        logits = self.linear_relu_stack(x)
        return logits