趋近智
尽管 Julia 在深度学习 (deep learning)方面拥有一个快速扩大的生态系统,但 Python 拥有一个成熟且丰富的库、预训练 (pre-training)模型和工具集,这些都是经过多年发展而来的。从 Julia 访问这些资源可以显著扩展您的能力,使您能够将 Python 的专门功能整合到基于 Julia 的深度学习项目中,而无需重复造轮子。PyCall.jl 是 Julia 与 Python 进行互操作性的主要包,它能实现这种整合。
PyCall.jl 允许您直接从 Julia 调用 Python 函数,在两种语言之间传递数据,并在 Julia 环境中管理 Python 对象。它充当一座桥梁,使得 Python 的库(包括机器学习 (machine learning)和数据科学中常见的 NumPy、SciPy、Pandas、Scikit-learn、PyTorch 和 TensorFlow/Keras 等)可供您的 Julia 程序使用。
在调用 Python 代码之前,您需要安装 PyCall.jl 并确保它能找到 Python 安装。
安装 PyCall.jl: 打开 Julia REPL 并使用包管理器:
using Pkg
Pkg.add("PyCall")
Python 安装:
默认情况下,PyCall.jl 将尝试使用它通过 Conda.jl 自动管理的 Conda Python 安装。当您首次 using PyCall 或尝试导入 Python 模块时,如果 PyCall 未找到合适的 Python 安装,它会提示您安装 Miniconda。这通常是开始使用的最简单方法。
另外,如果您有希望 PyCall.jl 使用的现有 Python 安装(例如系统 Python 或虚拟环境),您可以通过在 Julia 会话中首次加载 PyCall 之前设置 PYTHON 环境变量来配置它。例如,在您的 Julia 脚本或 REPL 中:
ENV["PYTHON"] = "/path/to/your/python/executable"
using PyCall
确保此路径指向 Python 可执行文件本身,而不仅仅是目录。
一旦 PyCall.jl 设置完成,您就可以开始导入和使用 Python 模块了。
通过 PyCall.jl 与 Python 库进行交互非常直接。
导入 Python 模块:
pyimport 函数用于导入 Python 模块。此函数返回一个 Julia 对象,该对象充当 Python 模块的代理。
using PyCall
# 导入 Python 的 'math' 模块
math = pyimport("math")
# 导入 NumPy
np = pyimport("numpy")
调用 Python 函数和访问属性: 您可以使用熟悉的 Julia 点语法调用这些代理对象的函数和访问其属性。
# 调用 Python math 模块的 sqrt 函数
py_sqrt_val = math.sqrt(25.0)
println("Python math.sqrt(25.0): $py_sqrt_val") # 输出: Python math.sqrt(25.0): 5.0
# 创建一个 NumPy 数组
py_array = np.array([1, 2, 3, 4])
println("NumPy array: $py_array")
# 访问一个属性(例如,NumPy 的 pi 常量)
py_pi = np.pi
println("NumPy pi: $py_pi")
PyCall.jl 会自动处理许多数据类型转换。例如,Julia 数字会转换为 Python 数字,Julia 字符串转换为 Python 字符串,Julia 数组通常转换为 NumPy 数组,反之亦然,当 Python 函数返回值时。
下面的图表展示了您的 Julia 代码使用 PyCall.jl 调用 Python 库时的典型交互路径。
当 Julia 通过 PyCall.jl 调用 Python 库时的交互流程。数据和函数调用在 Julia 和 Python 环境之间进行调度。
PyCall.jl 在深度学习中的一个重要用例是访问 Python 丰富的深度学习库,例如用于 NLP 模型的 transformers、用于图像处理工具的 scikit-image,甚至是来自 PyTorch 或 TensorFlow 的特定层或优化器,如果 Julia 中没有直接可用或合适的等效项。
例如,您可能希望使用 Hugging Face tokenizers 库中的先进分词 (tokenization)器 (tokenizer)。
首先,请确保 Python 库已安装在 PyCall.jl 所使用的 Python 环境中。如果 PyCall 管理自己的 Conda 环境,您可以使用 Conda.jl 将包安装到其中:
using Conda
Conda.add("tokenizers", channel="huggingface") # Hugging Face tokenizers 的示例
Conda.add("torch") # PyTorch 的示例
然后,您可以导入并使用它:
using PyCall
# 从 Hugging Face Transformers 导入 AutoTokenizer(假设已安装)
# 注意:此处使用的是 Python 包名
try
transformers = pyimport("transformers")
AutoTokenizer = transformers.AutoTokenizer
# 加载预训练分词器
tokenizer_name = "bert-base-uncased"
tokenizer = AutoTokenizer.from_pretrained(tokenizer_name)
# 对一些文本进行分词
julia_text = "Hello, Julia and Python working together!"
encoded_input = tokenizer(julia_text, return_tensors="pt") # pt for PyTorch tensors
println("输入文本: $julia_text")
println("分词结果 (ID): $(encoded_input["input_ids"])")
# 输出将是一个封装了 PyTorch 张量的 PyObject。
# 您可能需要进一步转换它以在 Julia/Flux 中使用。
catch e
println("导入或使用 Python 库出错: $e")
println("请确保 'transformers' 和 'torch' Python 包已安装在 PyCall 的 Python 环境中。")
end
PyCall.jl 在 Julia 和 Python 之间的数据类型转换方面做得很好。
Array 通常会自动转换为 NumPy 数组,反之亦然。由于 Flux.jl 张量通常是 Julia Array,这非常方便。Dict(转换为 Python dict)和 Vector(转换为 Python list)通常都能处理。然而,有时您会从 Python 调用中接收到 PyObject。这是围绕 Python 对象的通用 Julia 封装器。您通常可以直接在后续调用其他 Python 函数时使用此 PyObject。如果您需要将 PyObject 转换为特定的 Julia 类型,可以使用 convert(JuliaType, py_object)。
np = pyimport("numpy")
py_list = np.array([10, 20, 30]) # 这会返回一个封装了 NumPy 数组的 PyObject
julia_vector = convert(Vector{Int}, py_list)
println("转换后的 Julia 向量: $julia_vector") # 输出: 转换后的 Julia 向量: [10, 20, 30]
请注意数据传输。尽管 PyCall.jl 效率高,但 Julia 和 Python 内存空间之间频繁的大量数据传输可能会引入性能开销。对于兼容类型和内存布局的 NumPy 数组和 Julia 数组,PyCall.jl 有时可以避免数据复制,使它们共享相同的底层内存。这对于大型数值数组尤其有用。
您可以在 Flux.jl 流水线的各个阶段整合 Python 组件:
albumentations)或特征提取。PyCall.jl 加载 PyTorch 模型,向其传递 Julia 数据(转换为 NumPy 数组或 PyTorch 张量),并获取预测结果。示例:将 Python 工具与 Flux 数据结合使用
假设您有一个来自 Flux 的张量,并希望在其上使用 NumPy 函数:
using Flux
using PyCall
np = pyimport("numpy")
# 一个 Flux 张量(它只是一个 Julia 数组)
flux_tensor = rand(Float32, 2, 3)
println("Flux 张量 (Julia 数组):\n$flux_tensor")
# PyCall 自动将 Julia 数组转换为 NumPy 数组以供 NumPy 函数使用
numpy_sum = np.sum(flux_tensor, axis=0) # 直接传递 Julia 数组
println("沿轴 0 求和(通过 NumPy):\n$numpy_sum")
# 结果 'numpy_sum' 是一个 PyObject(封装了 NumPy 数组)。
# 如果后续 Flux 操作需要,请将其转换回 Julia 数组。
julia_sum_vector = convert(Vector{Float32}, numpy_sum)
println("转换回 Julia 向量:\n$julia_sum_vector")
如果您通过 PyCall.jl 将此 flux_tensor 传递给 PyTorch 模型,您通常需要先将其转换为 PyTorch 张量,这通常通过 NumPy 数组进行:
# 假设已导入 'torch' 且 'flux_tensor' 是您的 Julia 数组
# 1. 将 Julia 数组转换为封装 NumPy 数组的 PyObject(通常是自动的,或使用 PyObject(flux_tensor))
# 2. 将 NumPy PyObject 转换为 PyTorch Tensor PyObject
# py_numpy_array = np.asarray(flux_tensor) # 确保它是一个 NumPy 数组
# py_torch_tensor = torch.from_numpy(py_numpy_array)
# 现在 py_torch_tensor 可以馈送给 PyTorch 模型了。
尽管 PyCall.jl 提供了很大的灵活性,但请记住以下几点:
Project.toml、Manifest.toml)和 Python(例如 Conda 环境、requirements.txt)的依赖会增加项目的复杂性。请清楚地记录两者的设置。PyCall.jl 的开销可以忽略不计,或者便利性高于性能成本。PyCall.jl 是连接 Julia 和 Python 生态系统的一个强大工具。通过了解如何有效地使用它,您可以在深度学习 (deep learning)项目中借鉴两种语言的优势,提高您的生产力,并扩展您可以处理的问题范围。然而,请始终权衡其优势与潜在的复杂性和性能考量,当原生 Julia 解决方案能高效满足您的需求时,应优先选择它们。
这部分内容有帮助吗?
© 2026 ApX Machine LearningAI伦理与透明度•