从炼金术到工程学:数据科学项目持续成功10条规则(最佳实践)
出自16世纪荷兰画家彼得·勃鲁盖尔(Pieter Bruegel the Elder)的《炼金术士的实验室/Alchemist's Laboratory》
在数据科学项目中,我们经常遇到各种脚本、notebook、Python代码、Java代码、SQL等大杂烩式组合在一起的工程项目。这些项目从各种来源提取数据,代码被复制并在各个地方经过微小调整后重新使用。有些项目有版本控制和自动化的打包脚本,而有些项目根本没有测试、持续集成和部署(CI/CD)。所有这些问题导致无法确保项目质量和可重复性,为项目失败埋下隐患。大部分数据科学项目都是无序堆积的大杂烩,在工程化管理方面存在明显不足。
随着数据科学(包括人工智能应用)的复杂性和重要性在当今企业中不断增加,许多数据科学项目在实际应用中遇到各种问题,导致项目失败或结果不可靠。为了保障数据项目的成功,DrivenData Lab团队在其电子书《The 10 Rules of Reliable Data Science》中总结了数据科学项目持续成功的十条规则(最佳实践)。这些规则为确保数据项目的成功提供了宝贵的指导,能够帮助数据科学家和团队提高工作效率,确保项目质量和可重复性。
规则1:有序开始,有序进行(Start Organized, Stay Organized)
“在数据准备过程中,经常出现‘管道丛林’的现象。这些管道可能随着新信号的识别和新信息源的添加而自然发展。如果不加以注意,最终为机器学习准备数据的系统可能会变成由抓取、连接和采样步骤组成的丛林,常常伴随着中间文件的输出。管理这些管道、检测错误和从失败中恢复都是困难且昂贵的……所有这些都会增加系统的技术债务,使得进一步的创新变得更加昂贵。” —— Sculley 等人, “Machine Learning: The High Interest Credit Card of Technical Debt” (2014)
以干净和逻辑清晰的结构开始数据科学项目,并保持这种有序性,有助于数据科学家理解、扩展和重现分析。
- 为什么要遵循这条规则?
- 防止混乱:如果没有明确的结构,项目中的代码和数据很快就会变得混乱,导致结果难以重现。
- 促进协作:一个组织良好的项目使他人更容易理解和贡献,促进更好的协作。
- 自我文档化:有序的代码本身就是一种文档,减少了大量文档的需求,使得以后返回项目时更容易上手。
- 如何实现这条规则:
- 使用模板:从像Cookiecutter Data Science这样的项目模板开始,它提供了合理且自我文档化的结构。
以下是Cookiecutter的项目结构:
├── LICENSE <- Open-source license if one is chosen├── Makefile <- Makefile with convenience commands like `make data` or `make train`├── README.md <- The top-level README for developers using this project.├── data│ ├── external <- Data from third party sources.│ ├── interim <- Intermediate data that has been transformed.│ ├── processed <- The final, canonical data sets for modeling.│ └── raw <- The original, immutable data dump.│├── docs <- A default mkdocs project; see www.mkdocs.org for details│├── models <- Trained and serialized models, model predictions, or model summaries│├── notebooks <- Jupyter notebooks. Naming convention is a number (for ordering),│ the creator's initials, and a short `-` delimited description, e.g.│ `1.0-jqp-initial-data-exploration`.│├── pyproject.toml <- Project configuration file with package metadata for │ {{ cookiecutter.module_name }} and configuration for tools like black│├── references <- Data dictionaries, manuals, and all other explanatory materials.│├── reports <- Generated analysis as HTML, PDF, LaTeX, etc.│ └── figures <- Generated graphics and figures to be used in reporting│├── requirements.txt <- The requirements file for reproducing the analysis environment, e.g.│ generated with `pip freeze > requirements.txt`│├── setup.cfg <- Configuration file for flake8│└── {{ cookiecutter.module_name }} <- Source code for use in this project. │ ├── __init__.py <- Makes {{ cookiecutter.module_name }} a Python module │ ├── config.py <- Store useful variables and configuration │ ├── dataset.py <- Scripts to download or generate data │ ├── features.py <- Code to create features for modeling │ ├── modeling │ ├── __init__.py │ ├── predict.py <- Code to run model inference with trained models │ └── train.py <- Code to train models │ └── plots.py <- Code to create visualizationste
这种标准化的项目结构有助于保持项目的有序性和可维护性,确保数据科学项目的成功。
规则2:所有数据都有来源,原始数据不可变(Everything Comes from Somewhere, and the Raw Data is Immutable)
“每一条知识都必须在系统内有一个唯一的、明确的、权威的表示。” —— Andy Hunt 和 Dave Thomas, 《The Pragmatic Programmer》
这条规则强调确保项目中的所有数据都可以追溯到其来源的重要性。原始数据应保持不变,任何转换或分析都应能从这一原始数据集中再现。
- 为什么要遵循这条规则?
- 可重现性:确保每一个结论或结果都可以通过一个清晰、不间断的转换链条追溯到最初的原始数据。
- 问责性:有助于验证数据的有效性及其衍生结果的可靠性。
- 清晰性:通过澄清每个数据的来源,减少模糊性。
- 如何实现这条规则?
- 追踪数据血缘:使用有向无环图(DAG)来追踪数据的依赖关系和所应用的转换。
- 保持原始数据不可变:将原始数据存储为只读格式,绝不修改它。任何清洗或转换应生成一个新的数据集。
- 记录数据获取过程:在README或其他可访问文件中记录数据的获取方式,包括任何预处理步骤。
- 使用依赖管理工具:使用像Apache Airflow或Prefect这样的工具来管理和可视化数据管道,确保可追溯性。
- 工具和软件包:
- Apache Airflow:用于创建和管理数据管道。
- Prefect:另一个数据管道管理工具,注重简洁和灵活性。
- DAGsHub:用于数据和机器学习模型的版本控制,以及代码管理。
通过确保所有数据都可以追溯到其来源并保持原始数据的不可变性,数据科学家可以提高分析的可重现性和可靠性,使他们的工作更值得信赖且更易于审计。
规则3:版本控制是基本的专业素养(Version Control is Basic Professionalism)
“如果你没有源代码控制,你会在让程序员协同工作时感到压力重重。程序员无法知道其他人做了什么。错误也无法轻易回滚。” —— Joel Spolsky, “The Joel Test: 12 Steps to Better Code”
这条规则强调使用像Git这样的版本控制系统(VCS)来管理代码和数据的变更。它确保所有修改都被追踪、可逆且可审查。
- 为什么要遵循这条规则?
- 协作:通过允许多个人在同一个项目上工作而不产生冲突,促进团队合作。
- 问责:追踪变更并识别是谁做了哪些修改,增强透明度。
- 可逆性:当出现问题时,可以轻松回滚到以前的版本。
- 审查和质量控制:使代码审查和审计成为可能,帮助维持高质量标准。
- 如何实现这条规则?
- 使用Git进行代码管理:定期将代码更改提交到Git仓库。使用分支来管理不同的功能或开发阶段。
- 避免在VCS中存储大数据:仅存储小型、很少更改的数据集。对于较大的数据集,使用DVC(数据版本控制)或Git LFS(大文件存储)等工具。
- 自动化版本控制:使用脚本或工具自动对数据集和模型进行版本控制,确保每次更改都被追踪。
- 代码审查实践:实施一个通过拉取请求进行代码审查的流程,确保所有更改都由至少一个团队成员审查。
- 记录变更:维护一个变更日志,记录项目中的重大变化和更新。
- 工具和软件包:
- Git:最广泛使用的版本控制系统,用于追踪代码更改。
- Git LFS:用于在Git中管理大文件。
- DVC:用于对数据、模型和管道进行版本控制,与代码一起管理。
- GitHub/GitLab/Bitbucket:提供仓库、代码审查工具和CI/CD集成的平台。
使用版本控制是任何专业数据科学项目的基本要求。它增强了协作、问责和质量控制,使代码和数据的管理和维护随着时间的推移变得更加容易。
规则4:Notebooks用于探索,源文件用于重复(Notebooks are for Exploration, Source Files are for Repetition)
“大多数我听到的关于Notebooks的抱怨,我认为是对它们用途的误解……Notebooks绝对不是让你像在编辑器里那样输入所有代码并弄得一团糟。” —— Mali Akmanalp
这条规则强调了Notebooks和源文件在数据科学项目中不同的用途。Notebooks适合进行探索性分析和可视化,而源文件则更适合可重现和自动化的任务。
- 为什么要遵循这条规则?
- 探索:Notebooks提供了一个交互环境,非常适合实验和可视化。
- 可重现性:当源文件经过良好组织和管理时,确保过程可以可靠地重复。
- 协作和审查:源文件更容易在版本控制系统中管理,促进代码审查和协作。
- 如何实现这条规则?
- 在Notebooks中进行探索性分析:使用Jupyter或R Notebooks进行初步数据探索、可视化和迭代分析。
- 提取常用函数:在开发可重用的函数和流程时,将它们从Notebooks中提取到源文件(如Python脚本)。
- 组织源代码:将这些脚本放在一个组织良好的目录结构中,如/src或/scripts。
- 版本控制:将这些源文件提交到版本控制系统,启用协作开发和代码审查。
- 测试:为源文件中的函数编写测试,确保它们在Notebooks环境外也能正常工作。
- 工具和软件包:
- Jupyter Notebooks:用于交互式数据分析和可视化。
- VS Code或PyCharm:用于开发和管理源文件。
- nbconvert:将JupyterNotebooks转换为脚本。
- pytest:用于测试从Notebooks中提取的Python代码。
- Git:用于管理Notebooks和源文件的版本控制。
Notebooks非常适合探索性和迭代分析,但关键函数应提取到源文件中以确保可重现性和可维护性。这种方法利用了两种环境的优势,促进了更清晰和更有条理的工作流程。
规则5:测试和健全性检查预防灾难(Tests and Sanity Checks Prevent Catastrophes)
“没有测试的代码是糟糕的代码。它无论写得多么好,无论多么漂亮、面向对象或者封装良好。有了测试,我们可以快速而可靠地改变代码的行为。没有测试,我们真的不知道我们的代码是变得更好还是更糟。” —— Michael Feathers, 《Working Effectively with Legacy Code》
这条规则强调在数据科学代码中编写测试和进行健全性检查的重要性,以确保正确性和可靠性。测试有助于及早发现错误,并确保代码按预期工作。
- 为什么要遵循这条规则?
- 错误预防:测试有助于在问题变得更严重之前捕捉错误。
- 信心:确保代码在各种条件下都能正确执行。
- 维护:通过测试能够捕捉到回归,使得修改和扩展代码库更加容易。
- 可重现性:确保结果能够长期可靠地重现。
- 如何实现这条规则?
- 编写单元测试:专注于为单个函数和组件编写测试,验证它们在隔离环境中的行为。
- 使用健全性检查:实施健全性检查和冒烟测试,验证数据和基本功能的有效性。
- 使用样本数据进行测试:使用小型、具有代表性的数据集创建测试,验证代码处理典型情景和边缘情况的能力。
- 自动化测试:使用持续集成工具将测试集成到开发工作流中,实现自动化运行测试。
- 文档化测试:清晰地记录每个测试验证的内容,便于他人理解和维护。
- 工具和软件包:
- pytest:用于在Python中编写和运行测试的框架。
- unittest:Python内置的测试模块。
- Hypothesis:用于Python中的基于属性的测试。
- tox:用于跨多个环境自动化测试。
- 持续集成(CI)工具:如GitHub Actions、Travis CI或Jenkins,用于自动化运行测试。
测试和健全性检查对于确保数据科学代码的正确性和可靠性至关重要。它们有助于及早发现错误,提升对代码行为的信心,并使代码库更易于维护和扩展。
规则6:大声失败,快速失败(Fail Loudly, Fail Quickly)
“这个问题在机器学习系统中比其他类型的系统更常见。假设正在进行联接的特定表不再更新。机器学习系统将进行调整,其行为将继续保持相当良好,逐渐衰减。有时会发现表格已经过时数月,简单的刷新比季度内的任何其他发布都提高了性能!” — Martin Zinkevich, 《Rules of Machine Learning/机器学习规则》
这条规则强调设计系统以在遇到意外情况时能够及时且显眼地失败的重要性。它倡导防御性编程实践,使错误变得显而易见且可以迅速采取措施。
- 为什么要遵循这条规则?
- 错误检测:帮助及时捕捉错误,防止错误扩散并导致更大的问题。
- 调试:便于识别和修复问题的根本原因。
- 可靠性:确保系统表现可预测,并以受控方式失败。
- 责任:提供清晰的错误消息,帮助开发人员理解问题的原因及如何修复。
- 如何实现这条规则?
- 验证假设:实施检查确保输入和中间结果符合预期条件。
- 使用断言:添加断言来强制执行关于数据和代码行为的假设。
- 记录错误:实施全面的日志记录,捕获关于错误及其上下文的详细信息。
- 引发异常:使用异常处理未预期的情况,并确保适当处理。
- 快速失败:设计系统立即检测和响应错误,必要时停止进一步执行。
- 工具和软件包:
- 日志记录库:如Python的内置日志记录模块,用于捕获详细的错误信息。
- assert:Python中的assert语句,用于强制执行条件。
- 错误处理库:如Python中的bulwark,用于执行数据验证和假设。
- 测试库:像pytest这样的工具,编写测试确保系统在无效条件下正确失败。
设计系统以大声失败和快速失败有助于及时捕捉和解决错误,提高可靠性和可维护性。通过强制执行假设并提供清晰的错误消息,开发人员可以确保问题能够有效检测和解决。
规则7:从原始数据到最终输出,项目运行完全自动化(Project Runs are Fully Automated from Raw Data to Final Outputs)
“即使人们记得这些步骤,他们也可能会自我安慰而跳过某些步骤。在复杂的过程中,毕竟,并不是所有的步骤总是很重要。……‘这以前从未成为问题,’人们会说。直到有一天它变成了问题。” — Atul Gawande, 《The Checklist Manifesto/清单宣言》
这条规则强调了自动化整个数据流水线的重要性,从原始数据到最终输出,确保流程可重复、可靠,并能够以最小的努力被任何人执行。
- 为什么要遵循这条规则?
- 可重复性:确保整个过程可以重复执行并得到相同的结果。
- 效率:通过自动化重复性任务节省时间。
- 减少错误:通过减少手动步骤降低人为错误的发生概率。
- 一致性:确保每次执行都遵循相同的步骤,产生一致的结果。
- 如何实现这条规则?
- 使用构建工具:实施类似GNU Make或Apache Airflow的工具来管理和自动化数据流水线。
- 为每个步骤编写脚本:创建用于数据提取、清洗、转换、建模和报告的脚本。
- 自动化环境设置:使用Docker或虚拟环境等工具确保可以重现分析环境。
- 文档化过程:在README或类似文件中清晰记录运行流水线所需的步骤和命令。
- 持续集成:集成CI/CD工具,以便在代码库进行更改时自动运行流水线。
- 工具和软件包:
- Apache Airflow:用于编排复杂的数据流水线。
- GNU Make:管理构建过程的简单而强大的工具。
- Docker:用于容器化环境,确保在不同设置中的一致性。
- Vagrant:创建和配置轻量级、可重现和便携式工作环境。
- Jenkins/CircleCI/GitHub Actions:CI/CD工具,用于自动化运行流水线。
自动化从原始数据到最终输出的整个数据流水线,确保了可重复性、效率和一致性。它减少了人为错误的可能性,并使任何人都能轻松执行流程,从而产生更可靠和可信的结果。
规则8:重要参数提取和集中管理(Important Parameters are Extracted and Centralized)
“明确优于隐晦。” — Tim Peters, 《The Zen of Python/Python之禅》
这条规则侧重于在项目中集中和清晰定义重要参数,而不是将它们分散到代码中各处。这种做法提升了项目的清晰度、可重复性和修改的便捷性。
- 为什么要遵循这条规则?
- 清晰度:集中参数使得更容易理解项目的配置方式。
- 易于修改:在一个地方修改参数减少了不一致性和错误的风险。
- 文档化:集中的配置作为项目设置和参数的文档。
- 可重复性:确保所有参数都被明确设置和跟踪,使得结果更易重现。
- 如何实现这条规则?
- 使用配置文件:将参数存储在一个中心化的配置文件中(例如 config.yml、settings.json)。
- 环境变量:对于敏感信息或在不同环境中可能变化的参数,使用环境变量。
- 参数管理工具:使用能够管理参数并强制保持一致性的工具。
- 文档化参数:在配置文件或单独的文档文件中清晰地记录每个参数的作用和可能的取值。
- 集中访问:确保所有需要访问参数的代码部分从集中化的配置中读取参数。
- 工具和软件包:
- YAML/JSON/TOML:配置文件的格式。
- ConfigParser:Python模块,用于处理配置文件。
- dotenv:用于在 .env 文件中管理环境变量。
- Hydra:Python项目中管理配置文件的框架。
- Cerberus:Python中一个轻量级且可扩展的数据验证库。
集中和明确定义重要参数,提升了数据科学项目的清晰度、可维护性和可重复性。将所有配置集中在一处使得修改更易管理,项目也更加易于理解和可靠。
规则9:项目运行默认详细,并生成实质性产物(Project Runs are Verbose by Default and Result in Tangible Artifacts)
“在数据流水线运行期间捕获有用的输出,使得很容易找出结果的来源,便于回顾和继续上次的工作。” — DrivenData
这条规则强调了使数据流水线运行详细化,并确保其产生实质性的产物,记录了整个过程和结果。
- 为什么要遵循这条规则?
- 透明度:详细的日志和产物清晰地展示了结果如何获得。
- 调试:详细的输出帮助识别问题出现的位置。
- 文档化:自动生成的产物作为记录,有助于未来的重现和理解。
- 责任追溯:确保流水线的每一步都有记录,便于审查和审计。
- 如何实现这条规则?
- 启用详细日志记录:使用日志库捕获数据流水线每个步骤的详细信息。
- 生成产物:确保每次运行都生成产物,如日志、配置文件、中间数据集和最终结果。
- 时间戳和版本信息:在日志和产物中包含时间戳和版本信息,跟踪随时间的变化。
- 存储产物:将产物保存在结构化且易访问的位置,如版本控制的目录或云存储桶中。
- 文档化运行:为每次运行创建摘要报告,详细描述采取的步骤、使用的配置和获得的结果。
- 工具和软件包:
- logging:Python的内置日志模块,用于捕获详细日志。
- MLflow:用于管理机器学习生命周期的工具,包括实验跟踪、模型注册和产物存储。
- WandB:Weights & Biases,用于跟踪实验和可视化结果。
- TensorBoard:用于可视化TensorFlow日志。
- 结构化存储:如S3、Google Cloud Storage或Azure Blob Storage,用于存储产物。
通过使项目运行详细化并确保产生实质性的产物,可以提升透明度,便于调试,并提供全面的文档记录。这种做法使得理解、重现和建立在以前工作基础上更加容易,从而提升数据科学项目的整体可靠性和效率。
规则10:从最简单的端到端流水线开始(Start with the Simplest Possible End-to-End Pipeline)
“一个复杂系统的工作通常是从一个简单工作的系统演变而来的。从头设计的复杂系统从来都不会工作,并且不能通过补丁来使其工作。你必须从一个能工作的简单系统开始。” — Brian Kernighan 和 John Gall, Systemantics
这条规则强调了在逐步增加复杂性之前,从一个简单且功能完整的端到端流水线开始的重要性。首先建立一个最小可行产品(MVP),能够从原始数据处理到最终输出,然后逐步增强和优化。
- 为什么要遵循这条规则?
- 基础建设:建立一个工作的基准线,确保流程的各个部分都能连接和正常工作。
- 迭代改进:允许逐步完善和优化,减少引入错误的风险。
- 专注:在深陷细节和优化之前,帮助保持对主要目标的关注。
- 灵活性:提供一个灵活的框架,可以根据需要进行调整和扩展。
- 如何实现这条规则?
- 定义最小流水线:确定从原始数据处理到最终输出所需的基本步骤,并实施它们。
- 迭代开发:从最简单的实现开始,逐步添加功能、优化和复杂性。
- 早期验证:确保每个流水线阶段在进入下一阶段之前都能正常工作。
- 先用简单工具:最初使用简单且易理解的工具和方法,只有在必要时才引入更高级的技术。
- 文档化过程:随着每次迭代更新文档,确保不断演变的流水线仍然易于理解。
- 工具和软件包:
- Make:用于简单的构建自动化。
- Pandas:用于数据操作和初始数据处理。
- Scikit-learn:用于基本建模和机器学习任务。
- Jupyter Notebooks:用于原型设计和探索初始实现。
- Docker:用于创建可复现的环境。
从最简单的端到端流水线开始,确保了一个坚实的基础进行构建。这种方法允许逐步开发和完善,确保每个新增功能都建立在一个经过验证和功能完整的基础上,从而降低复杂性并提高可维护性。
数据科学项目的成功不仅依赖于技术和算法,更依赖于良好的工作实践和组织管理。通过遵循上述 10 个规则(最佳实践),数据科学家和团队可以提高工作效率,确保项目结果的可靠性和可重复性,为企业创造更大的价值。这些规则不仅适用于数据科学项目,也适用于任何需要高质量数据分析和处理的领域。希望这些最佳实践能对您的工作有所帮助,并期待与您共同探讨和改进数据科学工作流程。
《The 10 Rules of Reliable Data Science》 下载地址:https://drivendata.co/insights