Jiahong 的个人博客

凡事预则立,不预则废


  • Home

  • Tags

  • Archives

  • Navigation

  • Search

系统安装——Win10-U盘启动盘制作

由于 Windows10 过大,install.win 文件超过 4GB,常规的 U 盘启动盘制作都是面向 FAT 分区方式的,该方式无法存储超过 4GB 的文件,本文给出一种解决方案


Windows10 U 盘启动盘制作方法:

  • 打开 UltraISO 制作镜像
    • 注意不勾选 create boot partition 选项,否则 U 盘中会隐藏分区,看不到文件
  • 使用 convert H:/fs:ntfs(H 为 U 盘盘符,ntfs 可支持 4GB 以上的文件)
  • 解压 ISO 文件并替换 U 盘中的 ./sources/install.win 文件(因为该文件大于 4GB,FAT 分区格式无法写入)

Numpy——reshape函数


维度确定

规则为从前到后,参数由外到内,由行到列

  • values.reshape(2,3) 表示二行三列(2,3)
  • values.reshape(4,2,3) 表示四个(2,3),四个两行三列
  • values.reshape(5,4,2,3) 表示五个(4,2,3)

reshape本质理解

实际上numpy多维数组变化维度时,内存数据没有变化,只是有一个shape参数指明维度即可

  • 每次变化维度的过程可以理解为先将维度还原到(参考:维度确定)一维数组,然后再转化成制定的目标维度
  • 返回的是新对象,但是新老对象数据共享,只有shape等内部属性不一样,所以reshape操作不浪费内存,也不耗费时间,但是需要注意数据共享可能造成的误操作
    • 测试说明: 使用DataFrame.values.reshape()生成的新对象内存数据不共享

特殊参数 “-1“

用于智能补齐某一维度,只能有一个参数为-1

  • 除了-1参数外,其余的部分一定要能够被整除
  • -1本身占用一个维度
  • 假设一维数组维度为12
    • (-1, 2) <==> (6, 2) <==> (6, -1)
    • (-1) <==> (12)

Numpy——transpose函数

用于矩阵维度的转换


transpose函数的使用

  • 用法说明

    1
    nums.transpose(dims)
    • nums为np.ndarray类型的对象
    • dims为表示维度排列的tuple
      • 对于二维矩阵,只有0, 1两个维度,所以只能是(0,1), (1,0)
      • 三维矩阵,有0,1,2三个维度,所以可以是是(0,1,2)的所有全排列,共6个

简单易懂的解释

  • 只改变维度, 每一维度对应的数据不会变, 比如图片的shape为: (28, 28, 3),那么无论怎么变化维度, 变换后的数据中维度大小为3的那个维度都是表示通道, (28,28)总是表示图片的每个通道的数据
  • 原始numpy.ndarray,shape为(2,3,4)
    • .transpose((0,1,2)): shape为(2,3,4), 不变
    • .transpose((1,0,2)): shape为(3,2,4)
    • .transpose((2,1,0)): shape为(4,3,2)
  • 测试代码
    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
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    import numpy as np

    origin = np.random.random_integers(1, 100, (2,3,4))
    print("-"*20 + "origin")
    print(origin)
    print("-"*20 + "(0,1,2)")
    print(origin.transpose((0,1,2)))
    print("-"*20 + "(1,0,2)")
    print(origin.transpose((1,0,2)))
    print("-"*20 + "(2,1,0)")
    print(origin.transpose((2,1,0)))

    # Output:
    --------------------origin
    [[[50 36 80 53]
    [45 45 94 91]
    [49 29 69 53]]

    [[85 83 61 18]
    [16 89 80 60]
    [99 13 36 40]]]
    --------------------(0,1,2)
    [[[50 36 80 53]
    [45 45 94 91]
    [49 29 69 53]]

    [[85 83 61 18]
    [16 89 80 60]
    [99 13 36 40]]]
    --------------------(1,0,2)
    [[[50 36 80 53]
    [85 83 61 18]]

    [[45 45 94 91]
    [16 89 80 60]]

    [[49 29 69 53]
    [99 13 36 40]]]
    --------------------(2,1,0)
    [[[50 85]
    [45 16]
    [49 99]]

    [[36 83]
    [45 89]
    [29 13]]

    [[80 61]
    [94 80]
    [69 36]]

    [[53 18]
    [91 60]
    [53 40]]]

与reshape的区别

reshape的用法:
1
nums.reshape(shape)
* `nums`为`np.ndarray`类型的对象
* `shape`为表示维度排列的`tuple`
    * `shape`必须与原始`nums.shape`兼容,即每个`shape`元素相乘的结果相等
  • 从numpy中矩阵的存储开始讲起,numpy存储数据是C++的方式存储为一行的,然后分为不同shape以不同方式进行索引
  • reshape相当于是把原始数据先还原成一行(实际上没操作),然后再转变成指定shape的数据,本质上数据存储没变化,只是shape变了,以后索引方式也就变了
区别
  • reshape修改的只是shape,每个维度的意义变了
  • transpose修改的只是维度,同时shape跟着变化而已,每个维度的顺序变了,但是意义不会变

Sklearn——CountVectorizer使用介绍


导入

1
from sklearn.feature_extraction.text import CountVectorizer

使用示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# 新建一个CountVectorizer对象,以下简称CV对象
vectoerizer = CountVectorizer(min_df=min_df, max_df=max_df, stop_words=stop_words, token_pattern=r"(?u)\b[\w-][\w-]+\b")

# 使用文本集合(一个文本的列表)训练当前CountVectorizer对象
# texts = ["this is a text", "this is another text"]
vectoerizer.fit(texts)

# 训练后可以读取当前CV对象信息
bag_of_words = vectoerizer.get_feature_names()

# 使用CV对象生成任意文本集合的信息,返回对象是文档到字典的数组统计(统计单词数量)
# 这里的texts不必要是之前使用过的,可以是新的文本集合,但是词典已经确定,不能再拓展了,不存在字典中的词直接忽略
X = vectoerizer.transform(texts)

# 使用X对象,toarray()将返回 term/token counts 矩阵,可直接用于TfidfTransformer等对象的训练
X.toarray()

重要参数说明

  • min_df: 用于排除出现次数太少的terms

    • min_df = 0.01 意味着将忽略出现在少于%1的文档中的词
    • min_df = 5 意味着将忽略只出现在5篇以下文档中的词,不包括5篇
  • max_df:

    • max_df = 0.50 意味着将忽略出现在多于%50的文档中的词
    • max_df = 25 意味着将忽略出现在25篇以上文档中的词,不包括25篇

Python——抽象类和继承与Java的不同点

Python也开始支持抽象类和多态了,但是Python的继承与Java有很多不同的地方


Python只有私有属性

(双下划线__属性)和公有属性(默认属性),没有protected属性

  • 解决方案是Python提出命名时单下划线的属性为protected属性
    • 但是这只是一种口头约定,Python解释器并不做强制处理
  • 模块中的函数如果是单下划线开头的,那么该函数属于当前模块保护的
    • 此时某些IDE,比如Idea能够给出提示,但是用户仍可访问
    • 编程时建议将模块中不被调用的文件定义为_开头的,这样可以提示自己和别人当前函数不会在模块外被调用

Python类属性定义

属性定义在__init__函数中才能在对象中通过__dict__属性读取到


Python支持虚拟继承

可以欺骗编译器,不用真正实现虚拟基类的所有接口


都是强类型语言

这是他们的相同点,但我觉得写在这里是必要的


方法类型不同

  • Java中有一般方法(实例方法),静态方法(static类名和对象均可直接调用)两种
    • Java中这两种方法也都可以被继承和隐藏 ,而不能被重写(隐藏的意思时换成对应的父类指针还能访问到父类的静态属性和方法)
  • Python中有一般方法(实例方法),静态方法(@staticmethod类名和对象均可直接调用)和类方法@classmethod
    • 对@staticmethod方法的理解是,该方法与类密切相关,但是不需要访问这个类
    • Python的@staticmethod和@classmethod方法调用方式一样,唯一的区别在于@classmethod方法第一个参数必须是当前类(一般命名为cls)
    • Python中的这三种方法都可以被继承和重写

函数定义

  • 三种函数, 普通函数(实例函数), 类函数(classmethod), 静态函数(staticmethod)

普通函数

  • 无需任何装饰器
  • 无论如何定义,函数的第一个参数都会被当成实例对象本身(一般用self)

类函数

  • 装饰器 @classmethod
  • 无论如何定义,函数的第一个参数都会被当成实例对象本身(一般用cls)

静态函数

  • 装饰器 @staticmethod
  • 所有参数都是函数所有,不被占用

Python——Python中-m参数的使用


整体说明

  • 在 Python 中,-m 参数是一个非常有用的工具,它允许你将模块作为脚本运行,并且可以处理复杂的包结构。无论是运行标准库模块、自定义模块,还是与包结合使用,-m 参数都能简化操作。它的基本语法如下:

    1
    python -m module_name
  • 其中,module_name 是你要运行的模块名(不包含 .py 后缀)。Python 会在 sys.path 中查找该模块,并执行它


运行标准库模块

  • -m 参数常用于运行 Python 标准库中的模块。例如:

    1
    python -m http.server
  • 这会启动一个简单的 HTTP 服务器,默认监听在 8000 端口。http.server 是 Python 标准库中的一个模块,-m 参数让 Python 将其作为脚本运行


运行自定义模块

  • 你也可以使用 -m 参数运行自定义模块。假设你有一个项目结构如下:

    1
    2
    3
    4
    my_project/
    my_module/
    __init__.py
    main.py
  • 你可以在 my_project 目录下运行以下命令:

    1
    python -m my_module.main

这会执行 my_module/main.py 文件中的代码


运行包中的模块

  • 如果模块位于包中,-m 参数会自动处理包的导入路径。例如:

    1
    python -m package.subpackage.module
  • 这会执行 package/subpackage/module.py 文件中的代码


与 __main__.py 结合使用

  • 如果你有一个包,并且希望在运行包时执行特定的代码,可以在包目录下创建一个 __main__.py 文件。例如:

    1
    2
    3
    4
    my_package/
    __init__.py
    __main__.py
    other_module.py
  • 在 __main__.py 中编写你想要执行的代码,然后运行:

    1
    python -m my_package
    • Python
  • 这会执行 my_package/__main__.py 文件中的代码


与 python -c 结合使用

  • -m 参数也可以与 -c 参数结合使用,直接在命令行中运行模块。例如:

    1
    python -c "import os; print(os.getcwd())" -m http.server
    • 注:python -c "print('hello')"常常用来执行一个Python命令且不需要创建文件

Python——Python中没有Char类型


没有Char类型

不同于C++和Java等语言,Python中没有字符char类型,只有字符串类型


关于字符类型的操作

  • 把长度为1的字符串当成字符来操作,比如函数ord(s)中只要s的长度为1(len(s) == 1)即可,否则ord函数抛出异常
    • 长度为1的字符串本质上还是一个字符串类型<type str>
  • 判断字符串某一位置的字符时直接比较即可,如:
    1
    2
    3
    4
    5
    6
    7
    s = "12345"
    if s[2] == '2':
    print s[3]
    # 等价于
    if s[2] == "2":
    print s[3]
    # "2"和'2'都是<type str>类型的

Python——tqdm库使用


整体说明

  • Tqdm 是一个快速、可扩展的 Python 进度条库,可以轻松地为你的循环添加一个智能进度条,让你直观地了解任务的执行进度
  • Tqdm 的名字来源于阿拉伯语 “taqaddum”,意为“进展”
  • Tqdm 可以通过一行 pip 指令安装:
    1
    pip install tqdm

Tqdm 的最常用用法(自动控制)

  • Tqdm 最核心的用法就是将可迭代对象包装在 tqdm() 函数中

    1
    2
    3
    4
    5
    6
    7
    from tqdm import tqdm
    import time

    # 循环 100 次,每次暂停 0.01 秒
    for i in tqdm(range(100)):
    time.sleep(0.01)
    # 100%|██████████| 100/100 [00:01<00:00, 83.01it/s]
    • 运行这段代码,你会看到一个实时的进度条,显示循环的完成百分比、已完成的迭代次数、总迭代次数、每秒迭代次数以及预计剩余时间

Tqdm 的常用参数

  • desc: 给进度条添加一个描述性前缀

    1
    2
    3
    for i in tqdm(range(100), desc="Processing data"):
    time.sleep(0.01)
    # Processing data: 100%|██████████| 100/100 [00:01<00:00, 82.70it/s]
  • total: 当可迭代对象没有 __len__ 方法时,你可以手动指定总迭代次数

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    import random

    # 假设有一个生成器
    def my_generator():
    for _ in range(100):
    yield random.randint(1, 10)

    # 因为生成器没有长度,所以需要指定 total
    for item in tqdm(my_generator(), total=100):
    time.sleep(0.01)
    # 100%|██████████| 100/100 [00:01<00:00, 81.47it/s]
  • unit: 设置迭代单位,例如 'B'(字节)、'it'(迭代)

    1
    2
    3
    4
    # 模拟文件下载进度条
    for i in tqdm(range(1024), unit='B', unit_scale=True, desc="Downloading file"):
    time.sleep(0.001)
    # Downloading file: 100%|██████████| 1.02k/1.02k [00:12<00:00, 83.3B/s]
    • unit_scale=True 会自动将单位转换为 K、M、G 等,让显示更友好
  • ncols: 设置进度条的宽度,可以是一个整数或 None(自动适应终端宽度)

    1
    2
    3
    for i in tqdm(range(100), ncols=80): # 固定宽度为 80 个字符
    time.sleep(0.01)
    # 100%|█████████████████████████████████████████| 100/100 [00:01<00:00, 83.97it/s]
  • inital: 设置初始进度,用于从中间恢复任务的场景,一般用于手动控制的场景,下面会介绍


Tqdm 手动控制用法

  • 在某些情况下,可能无法直接将可迭代对象传递给 tqdm,例如当你需要在一个循环中分步更新进度时
  • 这时可以使用 tqdm 的上下文管理器或手动控制

使用上下文管理器 (推荐)

  • 使用 with 语句可以确保进度条在循环结束后正确关闭

    1
    2
    3
    4
    5
    6
    7
    with tqdm(total=100, desc="Manual loop") as pbar:
    for i in range(100):
    # 你的任务代码
    time.sleep(0.01)
    # 手动更新进度条
    pbar.update(1)
    # Manual loop: 100%|██████████| 100/100 [00:01<00:00, 82.50it/s]
    • pbar.update(n) 会将进度条前进 n 步,注意超过以后继续更新会导致超出部分显示异常(不会报错)
      1
      2
      3
      4
      5
      6
      7
      with tqdm(total=100, desc="Manual loop") as pbar:
      for i in range(100):
      # 你的任务代码
      time.sleep(0.01)
      # 手动更新进度条
      pbar.update(2)
      # Manual loop: 200it [00:01, 165.95it/s]
  • 若使用 initial 参数,则代码示例如下:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    from tqdm import tqdm
    import time

    with tqdm(initial=50, total=100, desc="Manual loop") as pbar:
    for i in range(50):
    # 你的任务代码
    time.sleep(0.01)
    # 手动更新进度条
    pbar.update(1)
    # 开始就是下面这样
    # Manual loop: 50%|█████ | 50/100 [00:00<?, ?it/s]
    # 最终变成这样:
    # Manual loop: 100%|██████████| 100/100 [00:00<00:00, 81.43it/s]

手动创建和关闭

  • 如果不能使用上下文管理器,可以手动创建和关闭进度条
    1
    2
    3
    4
    5
    6
    7
    8
    pbar = tqdm(total=100)
    for i in range(100):
    # 你的任务代码
    time.sleep(0.01)
    # 更新进度条
    pbar.update(1)
    # 任务完成后手动关闭进度条
    pbar.close()

Tqdm 高级用法

tqdm.notebook for Jupyter/IPython(暂未测试)

  • 如果在 Jupyter Notebook 或 IPython 环境中,可以使用 tqdm.notebook 模块,它会生成一个更美观的 HTML 进度条
    1
    2
    3
    4
    5
    from tqdm.notebook import tqdm
    import time

    for i in tqdm(range(100)):
    time.sleep(0.01)

tqdm.pandas for Pandas(暂未测试)

  • Tqdm 可以轻松地与 Pandas 的 apply、groupby 等方法结合,为数据处理过程添加进度条
    1
    2
    3
    4
    5
    6
    7
    8
    9
    import pandas as pd
    from tqdm.pandas import tqdm # 注意这里导入的是 tqdm.pandas 包

    tqdm.pandas(desc="Processing DataFrame")

    df = pd.DataFrame({'a': range(100000)})

    # 使用 progress_apply 替代 apply
    df['b'] = df['a'].progress_apply(lambda x: x * 2)

嵌套进度条

  • 当需要为嵌套循环添加进度条时,可以将内部循环的 tqdm 实例作为外部 tqdm 的子项

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    for i in tqdm(range(5), desc="Outer loop"):
    for j in tqdm(range(10), desc=f"Inner loop {i}", leave=False):
    time.sleep(0.01)
    # Outer loop: 0%| | 0/5 [00:00<?, ?it/s]
    # Inner loop 0: 0%| | 0/10 [00:00<?, ?it/s]
    # Inner loop 0: 90%|█████████ | 9/10 [00:00<00:00, 82.94it/s]
    # Outer loop: 20%|██ | 1/5 [00:00<00:00, 8.23it/s]
    # Inner loop 1: 0%| | 0/10 [00:00<?, ?it/s]
    # Inner loop 1: 90%|█████████ | 9/10 [00:00<00:00, 84.44it/s]
    # Outer loop: 40%|████ | 2/5 [00:00<00:00, 8.31it/s]
    # Inner loop 2: 0%| | 0/10 [00:00<?, ?it/s]
    # Inner loop 2: 90%|█████████ | 9/10 [00:00<00:00, 82.45it/s]
    # Outer loop: 60%|██████ | 3/5 [00:00<00:00, 8.26it/s]
    # Inner loop 3: 0%| | 0/10 [00:00<?, ?it/s]
    # Inner loop 3: 90%|█████████ | 9/10 [00:00<00:00, 80.50it/s]
    # Outer loop: 80%|████████ | 4/5 [00:00<00:00, 8.16it/s]
    # Inner loop 4: 0%| | 0/10 [00:00<?, ?it/s]
    # Inner loop 4: 80%|████████ | 8/10 [00:00<00:00, 79.85it/s]
    # Outer loop: 100%|██████████| 5/5 [00:00<00:00, 8.15it/s]
    • 注意 leave=False 参数,它会确保内部进度条在完成后立即消失,避免屏幕杂乱

Python——strip函数用法注意事项


整体说明

  • strip() 方法容易误解,使用时可能会发生未知问题,所以要小心
  • strip() 方法会从字符串的开头和结尾移除指定的字符,但它是逐个字符进行匹配的,而不是匹配整个子字符串
    • 即把输入参数当做一个字符集合(注意是一个字符集合而不是一个字符串)
    • 后续的文字只要在这个集合内都会被清除掉,知道遇到第一个不在这个字符集合的字符为止

具体示例分析

  • 下面是一个简单示例:

    1
    2
    string = "SYSTEM:You are an AI assistant. You will be given a task.  "
    print(string.strip('SYSTEM:'))
    • 上面的句子输出是 "ou are an AI assistant. You will be given a task. "(注意:"SYSTEM:Y" 都被删除了,不仅仅是 "SYSTEM:")
  • 具体来说,当执行 strip('SYSTEM:') 时:

    • strip() 会把 'SYSTEM:' 看作是一个字符集合 :{'S', 'Y', 'S', 'T', 'E', 'M', ':'}
    • 从字符串开头开始,逐个检查字符是否在这个集合中:
      • 'S' 在集合中,移除
      • 'Y' 在集合中,移除
      • 'S' 在集合中,移除
      • 'T' 在集合中,移除
      • 'E' 在集合中,移除
      • 'M' 在集合中,移除
      • ':' 在集合中,移除
      • 'Y' 在集合中,移除(这里是 “You” 的 ‘Y’)
      • 'o' 不在集合中,停止移除,后续的字符都不再移除
  • 所以最终结果是 "ou are an AI assistant. You will be given a task. "

  • 如果你想移除特定的前缀字符串,应该使用:

    1
    2
    3
    4
    5
    6
    7
    8
    9
    # 方法1:使用 removeprefix() (Python 3.9+)
    result = string.removeprefix('SYSTEM:')

    # 方法2:使用字符串切片
    if string.startswith('SYSTEM:'):
    result = string[7:] # 'SYSTEM:' 长度为7

    # 方法3:使用 replace() (但要小心,会替换所有匹配项)
    result = string.replace('SYSTEM:', '', 1) # 只替换第一个
  • 这样就能得到正确的结果:"You are an AI assistant. You will be given a task. "

Python——全局解释器锁


什么是 GIL?

  • 全局解释器锁(Global Interpreter Lock,GIL)是 Python 解释器(如 CPython)中的一个机制,它确保在同一时刻只有一个线程执行 Python 字节码
  • GIL 的存在意味着:即使在多核处理器上 ,多个线程也无法真正并行执行 Python 代码

GIL 存在的原因

  • GIL 的存在主要是为了保护 Python 解释器的内部状态,避免多线程同时修改导致的数据竞争问题
  • 由于 Python 的内存管理不是线程安全的,GIL 提供了一种简单的解决方案来保证线程安全

GIL对多线程的影响

  • GIL对多线程程序的影响取决于线程执行的任务类型:
    • CPU密集型任务 :这类任务主要是进行大量的计算,需要频繁使用CPU
      • GIL使得,即使使用多线程,多个 CPU 核心也无法同时执行 Python 代码
      • 多线程在 CPU 密集型任务上的表现可能比单线程更差,因为线程切换会带来额外的开销
    • I/O密集型任务 :这类任务主要是进行输入输出操作,如网络请求、文件读写等
      • 在执行 I/O 操作时,线程会释放 GIL,允许其他线程执行
      • 多线程在I/O密集型任务上可以显著提高性能

如何规避 GIL 的限制

  • 使用多进程(常用) :multiprocessing模块允许创建多个进程,每个进程都有自己的 Python 解释器和 GIL,因此可以真正并行执行 CPU 密集型任务
  • 使用 C 扩展(不常用) :将关键部分的代码用 C 语言实现,并在 C 扩展中释放 GIL。这样可以让C代码在多核处理器上并行执行
1…495051…66
Joe Zhou

Joe Zhou

Stay Hungry. Stay Foolish.

659 posts
53 tags
GitHub E-Mail
© 2026 Joe Zhou
Powered by Hexo
|
Theme — NexT.Gemini v5.1.4