C++和Java等语言都有++和–操作,为什么以方便自居的Python却没有这种操作呢?
Python的数值对象
1 | a = 1 |
- Python数值对象都是不可变类型,与String一样,所以不能修改对象内部数据
- C++中的
i++修改内存中对象本身,数值增加1,而Python不能修改对象本身 - 与C++中字符串可以修改,Python中不能修改是一个道理
凡事预则立,不预则废
C++和Java等语言都有++和–操作,为什么以方便自居的Python却没有这种操作呢?
1 | a = 1 |
i++修改内存中对象本身,数值增加1,而Python不能修改对象本身本文介绍Python中优先队列的用法
queue并不能算是Python标准库,所以在LeetCode等OJ环境中不能使用, 想要使用优先队列的话可以使用Python的标准库heapqheapq的使用请参考 Python——heapq模块-最大堆最小堆1 | from Queue import PriorityQueue |
queue包名已经弃用,测试发现本地Python2.7环境可以用,但是LeetCode线上环境不能用Queue1 | from Queue import PriorityQueue |
不能使用not pq这样的语句判断优先队列是否收敛,他不是普通的内嵌对象(list,str等是内嵌对象),除非pq == None否则,双端队列对象永远为not pq == False
下面用法最优:
1 | while pq.qsize(): |
PriorityQueue中默认递增排序(这一点与Python中的sorted函数和sort()函数一样),每次get(),移除并返回最小的对象
PriorityQueue中,可以同时添加不同类别的对象
PriorityQueue会将对象首先按照类别排序,然后各个类别内部按照不同数值排序
若传入对象是可以直接比较大小的类型即可直接传入,包括tuple, list, str, int(long)等类型
1 | ls = [(1, 'b'), (1, 'a'), (2, 'a'), [1, 'a'], [1, 'b'], [2, 'a'], '1a', '1b', '2a', 1, 2, 3, None] |
在做算法题时,没必要的情况下不建议使用
在做工程时建议使用这种方式
1 | import Queue |
注意__lt__函数中是小于号,说明递增排序,大于号,说明递减排序
PriorityQueue对象不是普通Python内嵌对象,不能使用Python内嵌的len函数
Python的list有很多强大的功能,有些比较罕见的操作可能很有用,需要我们记住
1 | l = [1, 2, 3] |
1 | l = [1, 2, 2, 3, 4] |
1 | l = [1, 2, 2, 3, 4, 5] |
本文介绍Python中双端队列(double-ended queue, 简称为deque)的用法
1
2
from collections import deque
import collections
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import collections
dq = collections.deque()
dq.append(3)
dq.append(4)
dq.append(1)
dq.appendleft(9)
dq.appendleft(10)
print dq
print dq.pop()
print dq
print dq.popleft()
print dq
while len(dq):
print dq.pop()
# # output:
# deque([10, 9, 3, 4, 1])
# 1
# deque([10, 9, 3, 4])
# 10
# deque([9, 3, 4])
# 4
# 3
# 9
qsize()函数,但是可以像普通队列一样使用Python内嵌的len函数pip install . 命令pip install . 命令时,Python 包管理工具 pip 会根据当前目录中的 setup.py 文件来安装包setup.py (setup.py 的详细示例见附录):pip 首先会查找并解析当前目录中的 setup.py 文件install_requires、extras_require 等)pip 会使用 setuptools 或 distutils 来构建包sdist)或一个二进制分发包(Binary Distribution,简称 wheel)pip 会根据 setup.py 中的 install_requires 列表安装包的核心依赖pip install .[dev]),pip 会安装对应的 extras_require 可选依赖extras_require 中的可选依赖pip 会将构建好的包安装到Python环境的 site-packages 目录中setup.py 中设置了 include_package_data=True 或指定了 package_data,这些数据文件也会被安装setup.py 中定义了 entry_points,对应的命令行工具会被安装并注册packages 或 find_packages() 指定的包和模块package_data 或 include_package_data 指定的额外数据文件setup.py 文件是 Python 项目中用于定义包的元数据、依赖关系及其他安装相关信息的脚本setup.py 文件主要通过 setuptools 或 distutils 库来实现包的配置和分发setup.py 文件基本结构示例: 1 | from setuptools import setup, find_packages |
name : 包的名称version : 包的版本号,通常遵循语义版本规范(如 MAJOR.MINOR.PATCH)author 和 author_email : 作者的姓名和联系邮箱description 和 long_description : 包的简短和详细描述url : 项目的主页或代码仓库地址packages : 指定需要包含的包列表,通常使用 find_packages() 自动发现package_data : 指定需要包含的非代码文件(如数据文件、模板等)install_requires : 安装包时需要满足的依赖列表extras_require : 可选依赖,按功能分组(如开发依赖、测试依赖)python_requires : 指定包支持的Python版本范围classifiers : 用于描述包的特性和分类,帮助用户和工具识别包的适用性entry_points : 定义包提供的命令行工具及其入口函数include_package_data : 是否包含包内的额外数据文件zip_safe : 指定包是否可以安全地打包为zip文件setup.py 中的 extras_require 使用在 setup.py 中定义的 extras_require 提供了一种机制,可以按需安装额外的依赖项
extras_require 允许定义一些可选的依赖组,比如开发依赖、测试依赖等
举例来说,前面附录小节的示例中 extras_require 中定义了一个名为 “dev” 的组和对应的依赖
默认情况下 ,安装包时不会自动安装 extras_require 中定义的可选依赖项
pip install . 或 pip install package_name 安装包即可install_requires 中定义的核心依赖会被安装安装可选依赖,以 安装 “dev” 组的可选依赖 为例:
使用 pip 安装时,可以通过在包名后加上 [dev] 来指定安装这些可选依赖
1 | pip install .[dev] |
如果包已经发布到PyPI或其他包管理平台,可以使用以下命令:
1 | pip install package_name[dev] |
Python中的装饰器可以在不修改原始函数代码的基础上,在Python函数中插入一些额外操作
装饰器定义
1 | def decorator(func): |
装饰器使用
1 | @decorator |
1 | def add(a, b): |
需要对装饰器进一步的封装
1 | def outterDecorator(tag): |
理解:
类装饰器的简单示例
1 | class Foo(object): |
如上述代码所示,类装饰器必须有__init__和__call__两个函数
__init__负责接受被装饰函数作为参数并存储该函数
__call__负责执行函数调用过程并执行想要插入函数的代码
被装饰的函数被调用时本质上是__call__函数被调用
1 | @a |
1 | f = a(b(c(f))) |
涉及到docstring,__name__等属性
1 | # 装饰器 |
使用functools.warps装饰器可以修复原始函数的文档
1 | from functools import wraps |
用于类的属性
1 | class Student(object): |
当加上property装饰器后,函数就变成了一个只读属性,被修饰的函数不能再当成普通函数
当前函数不能有参数,除非是默认参数,因为当前函数变成属性后,直接调用
1 | s.birth(10) |
s.birth返回一个属性值,然后,属性值不能被调用,所以抛出异常property装饰器会生成两个新的装饰[method_name].setter和[method_name].getter,分别用于代表当前函数对应属性的的写和读功能,读的功能默认加上了,写的功能需要的话我们可以使用[method_name].setter装饰器实现
总结: property装饰器可以将类的某个属性封装起来(在不暴露类属性的情况下提供getter方法和setter方法(后者需要自己显示定义))
*和**的主要用途有两个* 用于接收可变数量的位置参数,被称为 “可变位置参数”(variable positional arguments)** 用于接收可变数量的关键字参数,被称为 “可变关键字参数”(variable keyword arguments)*解包操作(序列解包)*操作符用于解包可迭代对象(如列表、元组、字符串、生成器等),将其元素单独提取出来1 | def add(a, b, c): |
1 | a, *b, c = [1, 2, 3, 4, 5] |
1 | # 合并列表 |
**解包操作(字典解包)**操作符用于解包字典 ,将其键值对作为参数传递给函数1 | def greet(name, age): |
1 | dict1 = {"a": 1, "b": 2} |
*args作为函数参数(可变位置参数)*args用于接收任意数量的位置参数(也称为非关键字参数),并将它们作为元组处理
*args作为函数参数 Demo
1 | def sum_numbers(*args): |
说明:
args是元组类型(解包时可以解所有序列,包括列表、元组、字符串、生成器等)args为空元组*args必须放在普通参数和**kwargs之间(如def func(a, *args, **kwargs))**kwargs作为函数参数(可变关键字参数)**kwargs用于接收任意数量的关键字参数 ,并将它们作为字典处理
**kwargs作为函数参数 Demo:
1 | def print_info(**kwargs): |
说明:
kwargs是字典类型kwargs为空字典def func(a, *args, b=1, **kwargs))*args和**kwargs函数可以同时接受*args和**kwargs,顺序必须是:def func(positional, *args, **kwargs)
组合使用*args和**kwargs的 Demo:
1 | def example(a, *args, **kwargs): |
说明:
普通参数也可以不传递,此时只剩下*args和**kwargs两种参数
在使用了**kwargs时,也可以继续使用关键字参数,但必须放到*args和**kwargs之间
1 | def example(a, *args, y=10, **kwargs) |
深入理解:放到*args和**kwargs之间的参数,必须按照关键字参数使用(定义时可以位置式的样子定义)
1 | def example(a, *args, n, **kwargs) |
1 | def my_decorator(func): |
1 | class Parent: |
*args是非关键字参数(也称为位置参数),对应参数args为元组类型,可表示任何多个无名参数
**kwargs是关键字参数 ,对应参数kwargs为字典(dict)类型,可表示任何多个命名参数
顺序必须是:1)普通参数;2)*args;3)**kwargs的形式
def func(a, *args, **kwargs)*args和**kwargs三个参数都是可选的,可以丢弃任意一个,但是相对顺序必须保障*args和**kwargs之间的参数一定是关键字参数将序列解包为函数参数时要保证参数数量匹配 :
1 | def func(a, b): |
将字典解包为函数参数时要保证键名匹配 :
1 | def func(name, age): |
*,则后面的所有参数必须通过关键字形式传入1 | # 调用 f 函数时,b 和 c 必须使用关键字传入,否则出错 |

图像分类 :SENet可以嵌入到各种经典的图像分类网络中,如ResNet、Inception等,通过对特征通道的自适应加权 ,能够更好地捕捉图像中的重要特征 ,抑制图片中的无关特征 ,从而提高分类准确率
目标检测 :在目标检测任务中,SENet有助于模型更准确地定位和识别目标物体。它可以增强与目标相关的特征通道 ,使模型对目标的细节和特征更加敏感,提高检测的精度和召回率
语义分割 :对于语义分割任务,SENet能够帮助模型更好地理解图像中的语义信息,通过调整通道权重 ,突出不同语义类别对应的特征 ,从而更精确地分割出不同的物体和区域
在 Inception 模块中的嵌入方式
在 Residual 模块中的嵌入方式
期望最大化(Exception Maximization Algorithm)EM算法
输入: 观测变量数据Y
输出: 模型(参数 \(\theta\))
E步: 计算 \(Q(\theta, \theta^{i})\)
$$
\begin{align}
Q(\theta, \theta^{i}) &= \mathbb{E}_{Z}[logP(Y,Z|\theta)|Y,\theta^{i}] \\
&= \mathbb{E}_{Z\sim P(Z|Y,\theta^{i})}[logP(Y,Z|\theta)] \\
&= \sum_{Z} P(Z|Y,\theta^{i})logP(Y,Z|\theta) \\
&= \sum_{Z} logP(Y,Z|\theta)P(Z|Y,\theta^{i})
\end{align}
$$
M步: 求使得 \(Q(\theta, \theta^{i})\) 极大化的参数 \(\theta=\theta^{i+1}\)
$$\theta^{i+1} = \mathop{\arg\max}_{\theta}Q(\theta, \theta^{i})$$
重复E步和M步,直到收敛
理解: \(Q(\theta, \theta^{i})\) 可以理解为 \(Q(\theta|\theta^{i})\),表示在参数 \(\theta^{i}\) 已知的情况下,对数似然函数关于隐变量后验分布的期望函数,函数的参数为 \(\theta\)
参考博客:https://www.jianshu.com/p/c3ff1ae5cb66
| 步骤 | 具体细节 |
|---|---|
| E步 | 基于 \(\theta^{i}\) 推断隐变量 \(Z\) 的期望,记为 \(Z^{i}\) |
| M步 | 基于已观测变量 \(Y\) 和 \(Z^{i}\) 对参数 \(\theta\) 做极大似然估计,得到 \(\theta^{i+1}\) $$\theta^{i+1}=\mathop{\arg\max}_{\theta}P(Y,Z^{i}\mid\theta)$$ |
| 步骤 | 具体细节 |
|---|---|
| E步 | 基于 \(\theta^{i}\) 推断隐变量 \(Z\) 的后验分布 \(P(Z\mid Y,\theta^{i})\) |
| E步M步均可 | 基于隐变量的后验分布 \(P(Z\mid Y,\theta^{i})\) 计算对数似然函数 \(logP(Y,Z\mid\theta)\) 关于隐变量 \(Z\) 的后验分布 \(P(Z\mid Y,\theta^{i})\) 的期望 \(Q(\theta,\theta^{i})\) $$Q(\theta,\theta^{i}) = \mathbb{E}_{P(Z\mid Y,\theta^{i})}logP(Y,Z\mid \theta)$$ |
| M步 | 基于期望函数 \(Q(\theta, \theta^{i})\),对参数 \(\theta\) 求极值(极大似然估计),得到$$\theta^{i+1}=\mathop{\arg\max}_{\theta}Q(\theta, \theta^{i})=\mathop{\arg\max}_{\theta}\mathbb{E}_{P(Z\mid Y,\theta^{i})}logP(Y,Z\mid \theta)$$ |
已知数据是观测数据Y,像极大似然法一样,使得似然函数最大化即可,这里为了方便计算使用对数似然
我们的终极目标与极大似然法一样,求一个使得似然函数最大(可能是极大)的参数$$\theta^{\star}=\mathop{\arg\max}_{\theta}L(\theta)$$
其中
$$
\begin{align}
L(\theta)&=logP(Y|\theta)\\
&=log\sum_{Z}P(Y,Z|\theta)\\
&=log\left(\sum_{Z}P(Y|Z,\theta)P(Z|\theta)\right)
\end{align}
$$
显然,上述似然函数比较难以求解,非凸,且涉及和(积分或加法)的对数等操作,难以展开(可能还有其他的原因)
所以我们使用EM算法迭代不断逼近原始似然函数的最优解 \(\theta^{\star}\) (注意:我们只能得到局部最优,但是一般来说局部最优也够用了)
可用Jensen不等式得到 \(L(\theta)\) 的下界来作为优化目标不断迭代
$$
\begin{align}
L(\theta)&=log\left(\sum_{Z}P(Y|Z,\theta)P(Z|\theta)\right)\\
&=log\left(\sum_{Z}P(Z|Y,\theta^{i})\frac{P(Y|Z,\theta)P(Z|\theta)}{P(Z|Y,\theta^{i})}\right)\\
&\geq \sum_{Z}P(Z|Y,\theta^{i})log\frac{P(Y|Z,\theta)P(Z|\theta)}{P(Z|Y,\theta^{i})}\\
&=B(\theta,\theta^{i})
\end{align}
$$
由于此时 \(B(\theta, \theta^{i})\) 是 \(L(\theta)\) 的下界(\(B(\theta, \theta^{i})\) 是固定 \(\theta^{i}\) 时关于 \(\theta\) 的凸函数且容易求导,可以求极大值),所以使得前者增大的参数 \(\theta\) 也能使得后者增大,为了使得后者尽可能的增大,我们对前者取极大值
$$
\begin{align}
\theta^{i+1} &= \mathop{\arg\max}_{\theta}B(\theta,\theta^{i})\\
&=\mathop{\arg\max}_{\theta}\left( \sum_{Z}P(Z|Y,\theta^{i})log\frac{P(Y|Z,\theta)P(Z|\theta)}{P(Z|Y,\theta^{i})}\right)\\
&=\mathop{\arg\max}_{\theta}\left( \sum_{Z}P(Z|Y,\theta^{i})logP(Y|Z,\theta)P(Z|\theta)-\sum_{Z}P(Z|Y,\theta^{i})logP(Z|Y,\theta^{i})\right)\\
&= \mathop{\arg\max}_{\theta}\left( \sum_{Z}P(Z|Y,\theta^{i})logP(Y|Z,\theta)P(Z|\theta)\right)\\
&=\mathop{\arg\max}_{\theta}Q(\theta,\theta^{i})
\end{align}
$$
初始化参数 \(\theta^{0}\)
1.根据参数 \(\theta^{i}\) 计算当前隐变量的分布函数 \(Q_{i}(Z)=P(Z|Y,\theta^{i})\)
2.根据 \(Q_{i}(Z)=P(Z|Y,\theta^{i})\) 得到对数似然函数下界函数 \(B(\theta, \theta^{i})\) (求原始似然函数的下界B函数是因为直接对原始似然函数求极大值很难)或者 \(Q(\theta,\theta^{i})\)
$$\mathop{\arg\max}_{\theta}B(\theta, \theta^{i})=\mathop{\arg\max}_{\theta}Q(\theta,\theta^{i})$$
(\(Q(\theta,\theta^{i})\) 可以看作是 \(B(\theta, \theta^{i})\) 的简化版, \(B(\theta, \theta^{i})\) 才是原始似然函数的下界, \(Q(\theta,\theta^{i})\) 不是原始似然函数的下界)
3.求使得函数 \(B(\theta, \theta^{i})\) 极大化的参数 \(\theta=\theta^{i+1}\)
4.循环1,2,3直到收敛(相邻两次参数的变化或者是似然函数的变化足够小即可判断为收敛)
总结:
F函数极大-极大法