diff --git a/.github/ISSUE_TEMPLATE/translations.md b/.github/ISSUE_TEMPLATE/translations.md
index caf57d9cf..e9bfb323a 100644
--- a/.github/ISSUE_TEMPLATE/translations.md
+++ b/.github/ISSUE_TEMPLATE/translations.md
@@ -1,109 +1,109 @@
----
-name: Translation template
-about: 🤝 Translating the course to another language
-title: ''
-labels: translation
-assignees: ''
-
----
-
-
-
-Hi there 👋
-
-Let's translate the course to `YOUR-LANG` so that the whole community can benefit from this resource 🌎!
-
-Below are the chapters and files that need translating - let us know here if you'd like to translate any and we'll add your name to the list. Once you're finished, open a pull request and tag this issue by including `#issue-number` in the description, where `issue-number` is the number of this issue.
-
-> 🙋 If you'd like others to help you with the translation, you can also post in our [forums](https://discuss.huggingface.co/c/course/20) or tag [@_lewtun](https://twitter.com/_lewtun) on Twitter to gain some visibility.
-
-## Chapters
-
-**0 - Setup**
-- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter0/1.mdx)
-
-**1 - Transformer models**
-- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/1.mdx)
-- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/2.mdx)
-- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/3.mdx)
-- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/4.mdx)
-- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/5.mdx)
-- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/6.mdx)
-- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/7.mdx)
-- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/8.mdx)
-- [ ] [`9.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/9.mdx)
-- [ ] [`10.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/10.mdx)
-
-**2 - Using 🤗 Transformers**
-- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/1.mdx)
-- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/2.mdx)
-- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/3.mdx)
-- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/4.mdx)
-- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/5.mdx)
-- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/6.mdx)
-- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/7.mdx)
-- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/8.mdx)
-
-**3 - Fine-tuning a pretrained model**
-- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/1.mdx)
-- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/2.mdx)
-- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/3.mdx)
-- [ ] [`3_tf.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/3_tf.mdx)
-- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/4.mdx)
-- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/5.mdx)
-- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/6.mdx)
-
-**4 - Sharing models and tokenizers**
-- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/1.mdx)
-- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/2.mdx)
-- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/3.mdx)
-- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/4.mdx)
-- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/5.mdx)
-- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/6.mdx)
-
-**5 - The 🤗 Datasets library**
-- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/1.mdx)
-- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/2.mdx)
-- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/3.mdx)
-- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/4.mdx)
-- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/5.mdx)
-- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/6.mdx)
-- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/7.mdx)
-- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/8.mdx)
-
-**6 - The 🤗 Tokenizers library**
-- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/1.mdx)
-- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/2.mdx)
-- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/3.mdx)
-- [ ] [`3b.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/3b.mdx)
-- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/4.mdx)
-- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/5.mdx)
-- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/6.mdx)
-- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/7.mdx)
-- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/8.mdx)
-- [ ] [`9.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/9.mdx)
-- [ ] [`10.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/10.mdx)
-
-**7 - Main NLP tasks**
-- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/1.mdx)
-- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/2.mdx)
-- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/3.mdx)
-- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/4.mdx)
-- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/5.mdx)
-- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/6.mdx)
-- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/7.mdx)
-- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/8.mdx)
-- [ ] [`9.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/9.mdx)
-
-**8 - How to ask for help**
-- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/1.mdx)
-- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/2.mdx)
-- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/3.mdx)
-- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/4.mdx)
-- [ ] [`4_tf.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/4_tf.mdx)
-- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/5.mdx)
-- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/6.mdx)
-- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/7.mdx)
-
-**Events**
+---
+name: Translation template
+about: 🤝 Translating the course to another language
+title: ''
+labels: translation
+assignees: ''
+
+---
+
+
+
+Hi there 👋
+
+Let's translate the course to `YOUR-LANG` so that the whole community can benefit from this resource 🌎!
+
+Below are the chapters and files that need translating - let us know here if you'd like to translate any and we'll add your name to the list. Once you're finished, open a pull request and tag this issue by including `#issue-number` in the description, where `issue-number` is the number of this issue.
+
+> 🙋 If you'd like others to help you with the translation, you can also post in our [forums](https://discuss.huggingface.co/c/course/20) or tag [@_lewtun](https://twitter.com/_lewtun) on Twitter to gain some visibility.
+
+## Chapters
+
+**0 - Setup**
+- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter0/1.mdx)
+
+**1 - Transformer models**
+- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/1.mdx)
+- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/2.mdx)
+- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/3.mdx)
+- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/4.mdx)
+- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/5.mdx)
+- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/6.mdx)
+- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/7.mdx)
+- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/8.mdx)
+- [ ] [`9.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/9.mdx)
+- [ ] [`10.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter1/10.mdx)
+
+**2 - Using 🤗 Transformers**
+- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/1.mdx)
+- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/2.mdx)
+- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/3.mdx)
+- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/4.mdx)
+- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/5.mdx)
+- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/6.mdx)
+- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/7.mdx)
+- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter2/8.mdx)
+
+**3 - Fine-tuning a pretrained model**
+- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/1.mdx)
+- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/2.mdx)
+- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/3.mdx)
+- [ ] [`3_tf.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/3_tf.mdx)
+- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/4.mdx)
+- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/5.mdx)
+- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter3/6.mdx)
+
+**4 - Sharing models and tokenizers**
+- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/1.mdx)
+- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/2.mdx)
+- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/3.mdx)
+- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/4.mdx)
+- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/5.mdx)
+- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter4/6.mdx)
+
+**5 - The 🤗 Datasets library**
+- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/1.mdx)
+- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/2.mdx)
+- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/3.mdx)
+- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/4.mdx)
+- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/5.mdx)
+- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/6.mdx)
+- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/7.mdx)
+- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter5/8.mdx)
+
+**6 - The 🤗 Tokenizers library**
+- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/1.mdx)
+- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/2.mdx)
+- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/3.mdx)
+- [ ] [`3b.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/3b.mdx)
+- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/4.mdx)
+- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/5.mdx)
+- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/6.mdx)
+- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/7.mdx)
+- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/8.mdx)
+- [ ] [`9.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/9.mdx)
+- [ ] [`10.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter6/10.mdx)
+
+**7 - Main NLP tasks**
+- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/1.mdx)
+- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/2.mdx)
+- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/3.mdx)
+- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/4.mdx)
+- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/5.mdx)
+- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/6.mdx)
+- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/7.mdx)
+- [ ] [`8.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/8.mdx)
+- [ ] [`9.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter7/9.mdx)
+
+**8 - How to ask for help**
+- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/1.mdx)
+- [ ] [`2.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/2.mdx)
+- [ ] [`3.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/3.mdx)
+- [ ] [`4.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/4.mdx)
+- [ ] [`4_tf.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/4_tf.mdx)
+- [ ] [`5.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/5.mdx)
+- [ ] [`6.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/6.mdx)
+- [ ] [`7.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/chapter8/7.mdx)
+
+**Events**
- [ ] [`1.mdx`](https://github.com/huggingface/course/blob/main/chapters/en/event/1.mdx)
\ No newline at end of file
diff --git a/.github/workflows/build_documentation.yml b/.github/workflows/build_documentation.yml
index a3e462fbe..339d7a254 100644
--- a/.github/workflows/build_documentation.yml
+++ b/.github/workflows/build_documentation.yml
@@ -14,6 +14,6 @@ jobs:
package: course
path_to_docs: course/chapters/
additional_args: --not_python_module
- languages: ar bn de en es fa fr gj he hi id it ja ko my ne pl pt ru ro te th tr vi zh-CN zh-TW
+ languages: ar bn de en es fa fr gj he hi id it ja ko ml my ne pl pt ru ro te th tr vi zh-CN zh-TW
secrets:
hf_token: ${{ secrets.HF_DOC_BUILD_PUSH }}
diff --git a/.github/workflows/build_pr_documentation.yml b/.github/workflows/build_pr_documentation.yml
index 7bceda10f..dde410abc 100644
--- a/.github/workflows/build_pr_documentation.yml
+++ b/.github/workflows/build_pr_documentation.yml
@@ -16,4 +16,4 @@ jobs:
package: course
path_to_docs: course/chapters/
additional_args: --not_python_module
- languages: ar bn de en es fa fr gj he hi id it ja ko my ne pl pt ru ro te th tr vi zh-CN zh-TW
+ languages: ar bn de en es fa fr gj he hi id it ja ko ml my ne pl pt ru ro te th tr vi zh-CN zh-TW
diff --git a/.github/workflows/quality.yml b/.github/workflows/quality.yml
index 58d381cf4..b81b67485 100644
--- a/.github/workflows/quality.yml
+++ b/.github/workflows/quality.yml
@@ -1,21 +1,21 @@
-name: Quality Check
-
-on:
- push:
- branches:
- - main
- pull_request:
-
-jobs:
- quality:
- runs-on: ubuntu-latest
- steps:
- - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
- - name: Set up Python 3.8
- uses: actions/setup-python@e9aba2c848f5ebd159c070c61ea2c4e2b122355e # v2
- with:
- python-version: 3.8
- - name: Install Python dependencies
- run: pip install black
- - name: Run Quality check
- run: make quality
+name: Quality Check
+
+on:
+ push:
+ branches:
+ - main
+ pull_request:
+
+jobs:
+ quality:
+ runs-on: ubuntu-latest
+ steps:
+ - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
+ - name: Set up Python 3.8
+ uses: actions/setup-python@e9aba2c848f5ebd159c070c61ea2c4e2b122355e # v2
+ with:
+ python-version: 3.8
+ - name: Install Python dependencies
+ run: pip install black
+ - name: Run Quality check
+ run: make quality
diff --git a/.github/workflows/upload_pr_documentation.yml b/.github/workflows/upload_pr_documentation.yml
index 130437218..98e4c728a 100644
--- a/.github/workflows/upload_pr_documentation.yml
+++ b/.github/workflows/upload_pr_documentation.yml
@@ -1,17 +1,17 @@
-name: Upload PR Documentation
-
-on:
- workflow_run:
- workflows: ["Build PR Documentation"]
- types:
- - completed
-
-jobs:
- build:
- uses: huggingface/doc-builder/.github/workflows/upload_pr_documentation.yml@9ad2de8582b56c017cb530c1165116d40433f1c6 # main
- with:
- package_name: course
- hub_base_path: https://moon-ci-docs.huggingface.co
- secrets:
- hf_token: ${{ secrets.HF_DOC_BUILD_PUSH }}
+name: Upload PR Documentation
+
+on:
+ workflow_run:
+ workflows: ["Build PR Documentation"]
+ types:
+ - completed
+
+jobs:
+ build:
+ uses: huggingface/doc-builder/.github/workflows/upload_pr_documentation.yml@9ad2de8582b56c017cb530c1165116d40433f1c6 # main
+ with:
+ package_name: course
+ hub_base_path: https://moon-ci-docs.huggingface.co
+ secrets:
+ hf_token: ${{ secrets.HF_DOC_BUILD_PUSH }}
comment_bot_token: ${{ secrets.COMMENT_BOT_TOKEN }}
\ No newline at end of file
diff --git a/.gitignore b/.gitignore
index a1d21056b..87cb9bb2d 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,84 +1,84 @@
-.vscode
-.idea/
-
-# Logs
-logs
-*.log
-npm-debug.log*
-yarn-debug.log*
-yarn-error.log*
-
-# Runtime data
-pids
-*.pid
-*.seed
-*.pid.lock
-
-# Directory for instrumented libs generated by jscoverage/JSCover
-lib-cov
-
-# Coverage directory used by tools like istanbul
-coverage
-
-# nyc test coverage
-.nyc_output
-
-# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
-.grunt
-
-# Bower dependency directory (https://bower.io/)
-bower_components
-
-# node-waf configuration
-.lock-wscript
-
-# Compiled binary addons (http://nodejs.org/api/addons.html)
-build/Release
-
-# Dependency directories
-node_modules/
-jspm_packages/
-
-# Typescript v1 declaration files
-typings/
-
-# Optional npm cache directory
-.npm
-
-# Optional eslint cache
-.eslintcache
-
-# Optional REPL history
-.node_repl_history
-
-# Output of 'npm pack'
-*.tgz
-
-# dotenv environment variables file
-.env
-
-# gatsby files
-.cache/
-public
-
-# Mac files
-.DS_Store
-
-# Yarn
-yarn-error.log
-yarn.lock
-.pnp/
-.pnp.js
-# Yarn Integrity file
-.yarn-integrity
-
-# Sylvain notes folder
-notes
-
-# Ignore Colab notebooks
-nbs/
-
-# Byte-compiled
-__pycache__/
-.cache/
-
+.vscode
+.idea/
+
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+node_modules/
+jspm_packages/
+
+# Typescript v1 declaration files
+typings/
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# dotenv environment variables file
+.env
+
+# gatsby files
+.cache/
+public
+
+# Mac files
+.DS_Store
+
+# Yarn
+yarn-error.log
+yarn.lock
+.pnp/
+.pnp.js
+# Yarn Integrity file
+.yarn-integrity
+
+# Sylvain notes folder
+notes
+
+# Ignore Colab notebooks
+nbs/
+
+# Byte-compiled
+__pycache__/
+.cache/
+
diff --git a/LICENSE b/LICENSE
index 261eeb9e9..29f81d812 100644
--- a/LICENSE
+++ b/LICENSE
@@ -1,201 +1,201 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [yyyy] [name of copyright owner]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
+ Apache License
+ Version 2.0, January 2004
+ http://www.apache.org/licenses/
+
+ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
+
+ 1. Definitions.
+
+ "License" shall mean the terms and conditions for use, reproduction,
+ and distribution as defined by Sections 1 through 9 of this document.
+
+ "Licensor" shall mean the copyright owner or entity authorized by
+ the copyright owner that is granting the License.
+
+ "Legal Entity" shall mean the union of the acting entity and all
+ other entities that control, are controlled by, or are under common
+ control with that entity. For the purposes of this definition,
+ "control" means (i) the power, direct or indirect, to cause the
+ direction or management of such entity, whether by contract or
+ otherwise, or (ii) ownership of fifty percent (50%) or more of the
+ outstanding shares, or (iii) beneficial ownership of such entity.
+
+ "You" (or "Your") shall mean an individual or Legal Entity
+ exercising permissions granted by this License.
+
+ "Source" form shall mean the preferred form for making modifications,
+ including but not limited to software source code, documentation
+ source, and configuration files.
+
+ "Object" form shall mean any form resulting from mechanical
+ transformation or translation of a Source form, including but
+ not limited to compiled object code, generated documentation,
+ and conversions to other media types.
+
+ "Work" shall mean the work of authorship, whether in Source or
+ Object form, made available under the License, as indicated by a
+ copyright notice that is included in or attached to the work
+ (an example is provided in the Appendix below).
+
+ "Derivative Works" shall mean any work, whether in Source or Object
+ form, that is based on (or derived from) the Work and for which the
+ editorial revisions, annotations, elaborations, or other modifications
+ represent, as a whole, an original work of authorship. For the purposes
+ of this License, Derivative Works shall not include works that remain
+ separable from, or merely link (or bind by name) to the interfaces of,
+ the Work and Derivative Works thereof.
+
+ "Contribution" shall mean any work of authorship, including
+ the original version of the Work and any modifications or additions
+ to that Work or Derivative Works thereof, that is intentionally
+ submitted to Licensor for inclusion in the Work by the copyright owner
+ or by an individual or Legal Entity authorized to submit on behalf of
+ the copyright owner. For the purposes of this definition, "submitted"
+ means any form of electronic, verbal, or written communication sent
+ to the Licensor or its representatives, including but not limited to
+ communication on electronic mailing lists, source code control systems,
+ and issue tracking systems that are managed by, or on behalf of, the
+ Licensor for the purpose of discussing and improving the Work, but
+ excluding communication that is conspicuously marked or otherwise
+ designated in writing by the copyright owner as "Not a Contribution."
+
+ "Contributor" shall mean Licensor and any individual or Legal Entity
+ on behalf of whom a Contribution has been received by Licensor and
+ subsequently incorporated within the Work.
+
+ 2. Grant of Copyright License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ copyright license to reproduce, prepare Derivative Works of,
+ publicly display, publicly perform, sublicense, and distribute the
+ Work and such Derivative Works in Source or Object form.
+
+ 3. Grant of Patent License. Subject to the terms and conditions of
+ this License, each Contributor hereby grants to You a perpetual,
+ worldwide, non-exclusive, no-charge, royalty-free, irrevocable
+ (except as stated in this section) patent license to make, have made,
+ use, offer to sell, sell, import, and otherwise transfer the Work,
+ where such license applies only to those patent claims licensable
+ by such Contributor that are necessarily infringed by their
+ Contribution(s) alone or by combination of their Contribution(s)
+ with the Work to which such Contribution(s) was submitted. If You
+ institute patent litigation against any entity (including a
+ cross-claim or counterclaim in a lawsuit) alleging that the Work
+ or a Contribution incorporated within the Work constitutes direct
+ or contributory patent infringement, then any patent licenses
+ granted to You under this License for that Work shall terminate
+ as of the date such litigation is filed.
+
+ 4. Redistribution. You may reproduce and distribute copies of the
+ Work or Derivative Works thereof in any medium, with or without
+ modifications, and in Source or Object form, provided that You
+ meet the following conditions:
+
+ (a) You must give any other recipients of the Work or
+ Derivative Works a copy of this License; and
+
+ (b) You must cause any modified files to carry prominent notices
+ stating that You changed the files; and
+
+ (c) You must retain, in the Source form of any Derivative Works
+ that You distribute, all copyright, patent, trademark, and
+ attribution notices from the Source form of the Work,
+ excluding those notices that do not pertain to any part of
+ the Derivative Works; and
+
+ (d) If the Work includes a "NOTICE" text file as part of its
+ distribution, then any Derivative Works that You distribute must
+ include a readable copy of the attribution notices contained
+ within such NOTICE file, excluding those notices that do not
+ pertain to any part of the Derivative Works, in at least one
+ of the following places: within a NOTICE text file distributed
+ as part of the Derivative Works; within the Source form or
+ documentation, if provided along with the Derivative Works; or,
+ within a display generated by the Derivative Works, if and
+ wherever such third-party notices normally appear. The contents
+ of the NOTICE file are for informational purposes only and
+ do not modify the License. You may add Your own attribution
+ notices within Derivative Works that You distribute, alongside
+ or as an addendum to the NOTICE text from the Work, provided
+ that such additional attribution notices cannot be construed
+ as modifying the License.
+
+ You may add Your own copyright statement to Your modifications and
+ may provide additional or different license terms and conditions
+ for use, reproduction, or distribution of Your modifications, or
+ for any such Derivative Works as a whole, provided Your use,
+ reproduction, and distribution of the Work otherwise complies with
+ the conditions stated in this License.
+
+ 5. Submission of Contributions. Unless You explicitly state otherwise,
+ any Contribution intentionally submitted for inclusion in the Work
+ by You to the Licensor shall be under the terms and conditions of
+ this License, without any additional terms or conditions.
+ Notwithstanding the above, nothing herein shall supersede or modify
+ the terms of any separate license agreement you may have executed
+ with Licensor regarding such Contributions.
+
+ 6. Trademarks. This License does not grant permission to use the trade
+ names, trademarks, service marks, or product names of the Licensor,
+ except as required for reasonable and customary use in describing the
+ origin of the Work and reproducing the content of the NOTICE file.
+
+ 7. Disclaimer of Warranty. Unless required by applicable law or
+ agreed to in writing, Licensor provides the Work (and each
+ Contributor provides its Contributions) on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
+ implied, including, without limitation, any warranties or conditions
+ of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
+ PARTICULAR PURPOSE. You are solely responsible for determining the
+ appropriateness of using or redistributing the Work and assume any
+ risks associated with Your exercise of permissions under this License.
+
+ 8. Limitation of Liability. In no event and under no legal theory,
+ whether in tort (including negligence), contract, or otherwise,
+ unless required by applicable law (such as deliberate and grossly
+ negligent acts) or agreed to in writing, shall any Contributor be
+ liable to You for damages, including any direct, indirect, special,
+ incidental, or consequential damages of any character arising as a
+ result of this License or out of the use or inability to use the
+ Work (including but not limited to damages for loss of goodwill,
+ work stoppage, computer failure or malfunction, or any and all
+ other commercial damages or losses), even if such Contributor
+ has been advised of the possibility of such damages.
+
+ 9. Accepting Warranty or Additional Liability. While redistributing
+ the Work or Derivative Works thereof, You may choose to offer,
+ and charge a fee for, acceptance of support, warranty, indemnity,
+ or other liability obligations and/or rights consistent with this
+ License. However, in accepting such obligations, You may act only
+ on Your own behalf and on Your sole responsibility, not on behalf
+ of any other Contributor, and only if You agree to indemnify,
+ defend, and hold each Contributor harmless for any liability
+ incurred by, or claims asserted against, such Contributor by reason
+ of your accepting any such warranty or additional liability.
+
+ END OF TERMS AND CONDITIONS
+
+ APPENDIX: How to apply the Apache License to your work.
+
+ To apply the Apache License to your work, attach the following
+ boilerplate notice, with the fields enclosed by brackets "[]"
+ replaced with your own identifying information. (Don't include
+ the brackets!) The text should be enclosed in the appropriate
+ comment syntax for the file format. We also recommend that a
+ file or class name and description of purpose be included on the
+ same "printed page" as the copyright notice for easier
+ identification within third-party archives.
+
+ Copyright [yyyy] [name of copyright owner]
+
+ Licensed under the Apache License, Version 2.0 (the "License");
+ you may not use this file except in compliance with the License.
+ You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+ Unless required by applicable law or agreed to in writing, software
+ distributed under the License is distributed on an "AS IS" BASIS,
+ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ See the License for the specific language governing permissions and
+ limitations under the License.
diff --git a/Makefile b/Makefile
index 01af2a0aa..0580a6ef7 100644
--- a/Makefile
+++ b/Makefile
@@ -1,9 +1,9 @@
-.PHONY: quality style
-
-# Check code formatting
-quality:
- python utils/code_formatter.py --check_only
-
-# Format code samples automatically and check is there are any problems left that need manual fixing
-style:
- python utils/code_formatter.py
+.PHONY: quality style
+
+# Check code formatting
+quality:
+ python utils/code_formatter.py --check_only
+
+# Format code samples automatically and check is there are any problems left that need manual fixing
+style:
+ python utils/code_formatter.py
diff --git a/README.md b/README.md
index d26b326b1..f496c8950 100644
--- a/README.md
+++ b/README.md
@@ -1,183 +1,183 @@
-# The Hugging Face Course
-
-This repo contains the content that's used to create the **[Hugging Face course](https://huggingface.co/course/chapter1/1)**. The course teaches you about applying Transformers to various tasks in natural language processing and beyond. Along the way, you'll learn how to use the [Hugging Face](https://huggingface.co/) ecosystem — [🤗 Transformers](https://github.com/huggingface/transformers), [🤗 Datasets](https://github.com/huggingface/datasets), [🤗 Tokenizers](https://github.com/huggingface/tokenizers), and [🤗 Accelerate](https://github.com/huggingface/accelerate) — as well as the [Hugging Face Hub](https://huggingface.co/models). It's completely free and open-source!
-
-## 🌎 Languages and translations
-
-| Language | Source | Authors |
-|:------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [English](https://huggingface.co/course/en/chapter1/1) | [`chapters/en`](https://github.com/huggingface/course/tree/main/chapters/en) | [@sgugger](https://github.com/sgugger), [@lewtun](https://github.com/lewtun), [@LysandreJik](https://github.com/LysandreJik), [@Rocketknight1](https://github.com/Rocketknight1), [@sashavor](https://github.com/sashavor), [@osanseviero](https://github.com/osanseviero), [@SaulLu](https://github.com/SaulLu), [@lvwerra](https://github.com/lvwerra) |
-| [Bengali](https://huggingface.co/course/bn/chapter1/1) (WIP) | [`chapters/bn`](https://github.com/huggingface/course/tree/main/chapters/bn) | [@avishek-018](https://github.com/avishek-018), [@eNipu](https://github.com/eNipu) |
-| [German](https://huggingface.co/course/de/chapter1/1) (WIP) | [`chapters/de`](https://github.com/huggingface/course/tree/main/chapters/de) | [@JesperDramsch](https://github.com/JesperDramsch), [@MarcusFra](https://github.com/MarcusFra), [@fabridamicelli](https://github.com/fabridamicelli) |
-| [Spanish](https://huggingface.co/course/es/chapter1/1) (WIP) | [`chapters/es`](https://github.com/huggingface/course/tree/main/chapters/es) | [@camartinezbu](https://github.com/camartinezbu), [@munozariasjm](https://github.com/munozariasjm), [@fordaz](https://github.com/fordaz) |
-| [Persian](https://huggingface.co/course/fa/chapter1/1) (WIP) | [`chapters/fa`](https://github.com/huggingface/course/tree/main/chapters/fa) | [@jowharshamshiri](https://github.com/jowharshamshiri), [@schoobani](https://github.com/schoobani) |
-| [French](https://huggingface.co/course/fr/chapter1/1) | [`chapters/fr`](https://github.com/huggingface/course/tree/main/chapters/fr) | [@lbourdois](https://github.com/lbourdois), [@ChainYo](https://github.com/ChainYo), [@melaniedrevet](https://github.com/melaniedrevet), [@abdouaziz](https://github.com/abdouaziz) |
-| [Gujarati](https://huggingface.co/course/gu/chapter1/1) (WIP) | [`chapters/gu`](https://github.com/huggingface/course/tree/main/chapters/gu) | [@pandyaved98](https://github.com/pandyaved98) |
-| [Hebrew](https://huggingface.co/course/he/chapter1/1) (WIP) | [`chapters/he`](https://github.com/huggingface/course/tree/main/chapters/he) | [@omer-dor](https://github.com/omer-dor) |
-| [Hindi](https://huggingface.co/course/hi/chapter1/1) (WIP) | [`chapters/hi`](https://github.com/huggingface/course/tree/main/chapters/hi) | [@pandyaved98](https://github.com/pandyaved98) |
-| [Bahasa Indonesia](https://huggingface.co/course/id/chapter1/1) (WIP) | [`chapters/id`](https://github.com/huggingface/course/tree/main/chapters/id) | [@gstdl](https://github.com/gstdl) |
-| [Italian](https://huggingface.co/course/it/chapter1/1) (WIP) | [`chapters/it`](https://github.com/huggingface/course/tree/main/chapters/it) | [@CaterinaBi](https://github.com/CaterinaBi), [@ClonedOne](https://github.com/ClonedOne), [@Nolanogenn](https://github.com/Nolanogenn), [@EdAbati](https://github.com/EdAbati), [@gdacciaro](https://github.com/gdacciaro) |
-| [Japanese](https://huggingface.co/course/ja/chapter1/1) (WIP) | [`chapters/ja`](https://github.com/huggingface/course/tree/main/chapters/ja) | [@hiromu166](https://github.com/@hiromu166), [@younesbelkada](https://github.com/@younesbelkada), [@HiromuHota](https://github.com/@HiromuHota) |
-| [Korean](https://huggingface.co/course/ko/chapter1/1) (WIP) | [`chapters/ko`](https://github.com/huggingface/course/tree/main/chapters/ko) | [@Doohae](https://github.com/Doohae), [@wonhyeongseo](https://github.com/wonhyeongseo), [@dlfrnaos19](https://github.com/dlfrnaos19), [@nsbg](https://github.com/nsbg) |
-| [Portuguese](https://huggingface.co/course/pt/chapter1/1) (WIP) | [`chapters/pt`](https://github.com/huggingface/course/tree/main/chapters/pt) | [@johnnv1](https://github.com/johnnv1), [@victorescosta](https://github.com/victorescosta), [@LincolnVS](https://github.com/LincolnVS) |
-| [Russian](https://huggingface.co/course/ru/chapter1/1) (WIP) | [`chapters/ru`](https://github.com/huggingface/course/tree/main/chapters/ru) | [@pdumin](https://github.com/pdumin), [@svv73](https://github.com/svv73), [@blademoon](https://github.com/blademoon) |
-| [Telugu]( https://huggingface.co/course/te/chapter0/1 ) (WIP) | [`chapters/te`](https://github.com/huggingface/course/tree/main/chapters/te) | [@Ajey95](https://github.com/Ajey95), [@RahulKonda18](https://github.com/RahulKonda18)
-| [Thai](https://huggingface.co/course/th/chapter1/1) (WIP) | [`chapters/th`](https://github.com/huggingface/course/tree/main/chapters/th) | [@peeraponw](https://github.com/peeraponw), [@a-krirk](https://github.com/a-krirk), [@jomariya23156](https://github.com/jomariya23156), [@ckingkan](https://github.com/ckingkan) |
-| [Turkish](https://huggingface.co/course/tr/chapter1/1) (WIP) | [`chapters/tr`](https://github.com/huggingface/course/tree/main/chapters/tr) | [@tanersekmen](https://github.com/tanersekmen), [@mertbozkir](https://github.com/mertbozkir), [@ftarlaci](https://github.com/ftarlaci), [@akkasayaz](https://github.com/akkasayaz) |
-| [Vietnamese](https://huggingface.co/course/vi/chapter1/1) | [`chapters/vi`](https://github.com/huggingface/course/tree/main/chapters/vi) | [@honghanhh](https://github.com/honghanhh) |
-| [Chinese (simplified)](https://huggingface.co/course/zh-CN/chapter1/1) | [`chapters/zh-CN`](https://github.com/huggingface/course/tree/main/chapters/zh-CN) | [@zhlhyx](https://github.com/zhlhyx), [petrichor1122](https://github.com/petrichor1122), [@1375626371](https://github.com/1375626371) |
-| [Chinese (traditional)](https://huggingface.co/course/zh-TW/chapter1/1) (WIP) | [`chapters/zh-TW`](https://github.com/huggingface/course/tree/main/chapters/zh-TW) | [@davidpeng86](https://github.com/davidpeng86), [@thliang01](https://github.com/thliang01) |
-| [Romanian](https://huggingface.co/course/ro/chapter1/1) (WIP) | [`chapters/ro`](https://github.com/huggingface/course/tree/main/chapters/ro) | [@Sigmoid](https://github.com/SigmoidAI), [@eduard-balamatiuc](https://github.com/eduard-balamatiuc), [@FriptuLudmila](https://github.com/FriptuLudmila), [@tokyo-s](https://github.com/tokyo-s), [@hbkdesign](https://github.com/hbkdesign), [@grumpycatyo-collab](https://github.com/grumpycatyo-collab), [@Angroys](https://github.com/Angroys) |
-
-### Translating the course into your language
-
-As part of our mission to democratise machine learning, we'd love to have the course available in many more languages! Please follow the steps below if you'd like to help translate the course into your language 🙏.
-
-**🗞️ Open an issue**
-
-To get started, navigate to the [_Issues_](https://github.com/huggingface/course/issues) page of this repo and check if anyone else has opened an issue for your language. If not, open a new issue by selecting the _Translation template_ from the _New issue_ button.
-
-Once an issue is created, post a comment to indicate which chapters you'd like to work on and we'll add your name to the list.
-
-**🗣 Join our Discord**
-
-Since it can be difficult to discuss translation details quickly over GitHub issues, we have created dedicated channels for each language on our Discord server. If you'd like to join, follow the instructions at this channel 👉: [https://discord.gg/JfAtkvEtRb](https://discord.gg/JfAtkvEtRb)
-
-**🍴 Fork the repository**
-
-Next, you'll need to [fork this repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo). You can do this by clicking on the **Fork** button on the top-right corner of this repo's page.
-
-Once you've forked the repo, you'll want to get the files on your local machine for editing. You can do that by cloning the fork with Git as follows:
-
-```bash
-git clone https://github.com/YOUR-USERNAME/course
-```
-
-**📋 Copy-paste the English files with a new language code**
-
-The course files are organised under a main directory:
-
-* [`chapters`](https://github.com/huggingface/course/tree/main/chapters): all the text and code snippets associated with the course.
-
-You'll only need to copy the files in the [`chapters/en`](https://github.com/huggingface/course/tree/main/chapters/en) directory, so first navigate to your fork of the repo and run the following:
-
-```bash
-cd ~/path/to/course
-cp -r chapters/en/CHAPTER-NUMBER chapters/LANG-ID/CHAPTER-NUMBER
-```
-
-Here, `CHAPTER-NUMBER` refers to the chapter you'd like to work on and `LANG-ID` should be one of the ISO 639-1 or ISO 639-2 language codes -- see [here](https://www.loc.gov/standards/iso639-2/php/code_list.php) for a handy table.
-
-**✍️ Start translating**
-
-Now comes the fun part - translating the text! The first thing we recommend is translating the part of the `_toctree.yml` file that corresponds to your chapter. This file is used to render the table of contents on the website and provide the links to the Colab notebooks. The only fields you should change are the `title`, ones -- for example, here are the parts of `_toctree.yml` that we'd translate for [Chapter 0](https://huggingface.co/course/chapter0/1?fw=pt):
-
-```yaml
-- title: 0. Setup # Translate this!
- sections:
- - local: chapter0/1 # Do not change this!
- title: Introduction # Translate this!
-```
-
-> 🚨 Make sure the `_toctree.yml` file only contains the sections that have been translated! Otherwise you won't be able to build the content on the website or locally (see below how).
-
-
-Once you have translated the `_toctree.yml` file, you can start translating the [MDX](https://mdxjs.com/) files associated with your chapter.
-
-> 🙋 If the `_toctree.yml` file doesn't yet exist for your language, you can simply create one by copy-pasting from the English version and deleting the sections that aren't related to your chapter. Just make sure it exists in the `chapters/LANG-ID/` directory!
-
-**👷♂️ Build the course locally**
-
-Once you're happy with your changes, you can preview how they'll look by first installing the [`doc-builder`](https://github.com/huggingface/doc-builder) tool that we use for building all documentation at Hugging Face:
-
-```bash
-python -m venv .venv
-source .venv/bin/activate
-python -m pip install --upgrade pip
-python -m pip install "git+https://github.com/huggingface/doc-builder.git"
-```
-
-```bash
-doc-builder preview course ./chapters/LANG-ID --not_python_module
-```
-
-If `LANG-ID` content is partial, make sure `_toctree.yml` only references files that exist in `chapters/LANG-ID`.
-
-If preview starts but all routes return `404`, this might be a local `doc-builder` version mismatch between the Python package and the SvelteKit template:
-
-```bash
-python -m pip uninstall -y hf-doc-builder
-python -m pip install "git+https://github.com/huggingface/doc-builder.git"
-```
-
-Then restart `doc-builder preview`.
-
-> `preview` command does not work with Windows.
-
-This will build and render the course on [http://localhost:5173/](http://localhost:5173/). Although the content looks much nicer on the Hugging Face website, this step will still allow you to check that everything is formatted correctly.
-
-**🚀 Submit a pull request**
-
-If the translations look good locally, the final step is to prepare the content for a pull request. Here, the first think to check is that the files are formatted correctly. For that you can run:
-
-```
-pip install -r requirements.txt
-make style
-```
-
-Once that's run, commit any changes, open a pull request, and tag [@lewtun](https://github.com/lewtun) and [@stevhliu](https://github.com/stevhliu) for a review. If you also know other native-language speakers who are able to review the translation, tag them as well for help. Congratulations, you've now completed your first translation 🥳!
-
-> 🚨 To build the course on the website, double-check your language code exists in `languages` field of the `build_documentation.yml` and `build_pr_documentation.yml` files in the `.github` folder. If not, just add them in their alphabetical order.
-
-## 📔 Jupyter notebooks
-
-The Jupyter notebooks containing all the code from the course are hosted on the [`huggingface/notebooks`](https://github.com/huggingface/notebooks) repo. If you wish to generate them locally, first install the required dependencies:
-
-```bash
-python -m pip install -r requirements.txt
-```
-
-Then run the following script:
-
-```bash
-python utils/generate_notebooks.py --output_dir nbs
-```
-
-This script extracts all the code snippets from the chapters and stores them as notebooks in the `nbs` folder (which is ignored by Git by default).
-
-## ✍️ Contributing a new chapter
-
-> Note: we are not currently accepting community contributions for new chapters. These instructions are for the Hugging Face authors.
-
-Adding a new chapter to the course is quite simple:
-
-1. Create a new directory under `chapters/en/chapterX`, where `chapterX` is the chapter you'd like to add.
-2. Add numbered MDX files `sectionX.mdx` for each section. If you need to include images, place them in the [huggingface-course/documentation-images](https://huggingface.co/datasets/huggingface-course/documentation-images) repository and use the [HTML Images Syntax](https://www.w3schools.com/html/html_images.asp) with the path `https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/{langY}/{chapterX}/{your-image.png}`.
-3. Update the `_toctree.yml` file to include your chapter sections -- this information will render the table of contents on the website. If your section involves both the PyTorch and TensorFlow APIs of `transformers`, make sure you include links to both Colabs in the `colab` field.
-
-If you get stuck, check out one of the existing chapters -- this will often show you the expected syntax.
-
-Once you are happy with the content, open a pull request and tag [@lewtun](https://github.com/lewtun) for a review. We recommend adding the first chapter draft as a single pull request -- the team will then provide feedback internally to iterate on the content 🤗!
-
-## Deploying to hf.co/course (for HF staff)
-
-The course content is deployed to [hf.co/course](https://huggingface.co/learn/nlp-course/chapter1/1) by triggering the [GitHub CI](.github/workflows/build_documentation.yml) from the `release` branch. To trigger the build, first create a new branch from `main` that will be used to update the current state on `release`:
-
-```shell
-git checkout main
-git checkout -b bump_release
-```
-
-Next, resolve any conflicts between the `release` and `bump_release` branches. Since this is tiresome to do manually, we can do the following to accept the latest changes:
-
-```shell
-git checkout bump_release
-git merge -s ours release
-```
-
-Next, push the `bump_release` branch and open a PR against `release` (not `main`!). Here is an example [PR](https://github.com/huggingface/course/pull/768). Once the CI is green, merge the PR and this will trigger the GitHub CI to build the new course content. This takes around 10-15 minutes, after which the latest changes will be visible on [hf.co/course](https://huggingface.co/learn/nlp-course/chapter1/1)!
-
-
-## 🙌 Acknowledgements
-
-The structure of this repo and README are inspired by the wonderful [Advanced NLP with spaCy](https://github.com/ines/spacy-course) course.
+# The Hugging Face Course
+
+This repo contains the content that's used to create the **[Hugging Face course](https://huggingface.co/course/chapter1/1)**. The course teaches you about applying Transformers to various tasks in natural language processing and beyond. Along the way, you'll learn how to use the [Hugging Face](https://huggingface.co/) ecosystem — [🤗 Transformers](https://github.com/huggingface/transformers), [🤗 Datasets](https://github.com/huggingface/datasets), [🤗 Tokenizers](https://github.com/huggingface/tokenizers), and [🤗 Accelerate](https://github.com/huggingface/accelerate) — as well as the [Hugging Face Hub](https://huggingface.co/models). It's completely free and open-source!
+
+## 🌎 Languages and translations
+
+| Language | Source | Authors |
+|:------------------------------------------------------------------------------|:-----------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [English](https://huggingface.co/course/en/chapter1/1) | [`chapters/en`](https://github.com/huggingface/course/tree/main/chapters/en) | [@sgugger](https://github.com/sgugger), [@lewtun](https://github.com/lewtun), [@LysandreJik](https://github.com/LysandreJik), [@Rocketknight1](https://github.com/Rocketknight1), [@sashavor](https://github.com/sashavor), [@osanseviero](https://github.com/osanseviero), [@SaulLu](https://github.com/SaulLu), [@lvwerra](https://github.com/lvwerra) |
+| [Bengali](https://huggingface.co/course/bn/chapter1/1) (WIP) | [`chapters/bn`](https://github.com/huggingface/course/tree/main/chapters/bn) | [@avishek-018](https://github.com/avishek-018), [@eNipu](https://github.com/eNipu) |
+| [German](https://huggingface.co/course/de/chapter1/1) (WIP) | [`chapters/de`](https://github.com/huggingface/course/tree/main/chapters/de) | [@JesperDramsch](https://github.com/JesperDramsch), [@MarcusFra](https://github.com/MarcusFra), [@fabridamicelli](https://github.com/fabridamicelli) |
+| [Spanish](https://huggingface.co/course/es/chapter1/1) (WIP) | [`chapters/es`](https://github.com/huggingface/course/tree/main/chapters/es) | [@camartinezbu](https://github.com/camartinezbu), [@munozariasjm](https://github.com/munozariasjm), [@fordaz](https://github.com/fordaz) |
+| [Persian](https://huggingface.co/course/fa/chapter1/1) (WIP) | [`chapters/fa`](https://github.com/huggingface/course/tree/main/chapters/fa) | [@jowharshamshiri](https://github.com/jowharshamshiri), [@schoobani](https://github.com/schoobani) |
+| [French](https://huggingface.co/course/fr/chapter1/1) | [`chapters/fr`](https://github.com/huggingface/course/tree/main/chapters/fr) | [@lbourdois](https://github.com/lbourdois), [@ChainYo](https://github.com/ChainYo), [@melaniedrevet](https://github.com/melaniedrevet), [@abdouaziz](https://github.com/abdouaziz) |
+| [Gujarati](https://huggingface.co/course/gu/chapter1/1) (WIP) | [`chapters/gu`](https://github.com/huggingface/course/tree/main/chapters/gu) | [@pandyaved98](https://github.com/pandyaved98) |
+| [Hebrew](https://huggingface.co/course/he/chapter1/1) (WIP) | [`chapters/he`](https://github.com/huggingface/course/tree/main/chapters/he) | [@omer-dor](https://github.com/omer-dor) |
+| [Hindi](https://huggingface.co/course/hi/chapter1/1) (WIP) | [`chapters/hi`](https://github.com/huggingface/course/tree/main/chapters/hi) | [@pandyaved98](https://github.com/pandyaved98) |
+| [Bahasa Indonesia](https://huggingface.co/course/id/chapter1/1) (WIP) | [`chapters/id`](https://github.com/huggingface/course/tree/main/chapters/id) | [@gstdl](https://github.com/gstdl) |
+| [Italian](https://huggingface.co/course/it/chapter1/1) (WIP) | [`chapters/it`](https://github.com/huggingface/course/tree/main/chapters/it) | [@CaterinaBi](https://github.com/CaterinaBi), [@ClonedOne](https://github.com/ClonedOne), [@Nolanogenn](https://github.com/Nolanogenn), [@EdAbati](https://github.com/EdAbati), [@gdacciaro](https://github.com/gdacciaro) |
+| [Japanese](https://huggingface.co/course/ja/chapter1/1) (WIP) | [`chapters/ja`](https://github.com/huggingface/course/tree/main/chapters/ja) | [@hiromu166](https://github.com/@hiromu166), [@younesbelkada](https://github.com/@younesbelkada), [@HiromuHota](https://github.com/@HiromuHota) |
+| [Korean](https://huggingface.co/course/ko/chapter1/1) (WIP) | [`chapters/ko`](https://github.com/huggingface/course/tree/main/chapters/ko) | [@Doohae](https://github.com/Doohae), [@wonhyeongseo](https://github.com/wonhyeongseo), [@dlfrnaos19](https://github.com/dlfrnaos19), [@nsbg](https://github.com/nsbg) |
+| [Portuguese](https://huggingface.co/course/pt/chapter1/1) (WIP) | [`chapters/pt`](https://github.com/huggingface/course/tree/main/chapters/pt) | [@johnnv1](https://github.com/johnnv1), [@victorescosta](https://github.com/victorescosta), [@LincolnVS](https://github.com/LincolnVS) |
+| [Russian](https://huggingface.co/course/ru/chapter1/1) (WIP) | [`chapters/ru`](https://github.com/huggingface/course/tree/main/chapters/ru) | [@pdumin](https://github.com/pdumin), [@svv73](https://github.com/svv73), [@blademoon](https://github.com/blademoon) |
+| [Telugu]( https://huggingface.co/course/te/chapter0/1 ) (WIP) | [`chapters/te`](https://github.com/huggingface/course/tree/main/chapters/te) | [@Ajey95](https://github.com/Ajey95), [@RahulKonda18](https://github.com/RahulKonda18)
+| [Thai](https://huggingface.co/course/th/chapter1/1) (WIP) | [`chapters/th`](https://github.com/huggingface/course/tree/main/chapters/th) | [@peeraponw](https://github.com/peeraponw), [@a-krirk](https://github.com/a-krirk), [@jomariya23156](https://github.com/jomariya23156), [@ckingkan](https://github.com/ckingkan) |
+| [Turkish](https://huggingface.co/course/tr/chapter1/1) (WIP) | [`chapters/tr`](https://github.com/huggingface/course/tree/main/chapters/tr) | [@tanersekmen](https://github.com/tanersekmen), [@mertbozkir](https://github.com/mertbozkir), [@ftarlaci](https://github.com/ftarlaci), [@akkasayaz](https://github.com/akkasayaz) |
+| [Vietnamese](https://huggingface.co/course/vi/chapter1/1) | [`chapters/vi`](https://github.com/huggingface/course/tree/main/chapters/vi) | [@honghanhh](https://github.com/honghanhh) |
+| [Chinese (simplified)](https://huggingface.co/course/zh-CN/chapter1/1) | [`chapters/zh-CN`](https://github.com/huggingface/course/tree/main/chapters/zh-CN) | [@zhlhyx](https://github.com/zhlhyx), [petrichor1122](https://github.com/petrichor1122), [@1375626371](https://github.com/1375626371) |
+| [Chinese (traditional)](https://huggingface.co/course/zh-TW/chapter1/1) (WIP) | [`chapters/zh-TW`](https://github.com/huggingface/course/tree/main/chapters/zh-TW) | [@davidpeng86](https://github.com/davidpeng86), [@thliang01](https://github.com/thliang01) |
+| [Romanian](https://huggingface.co/course/ro/chapter1/1) (WIP) | [`chapters/ro`](https://github.com/huggingface/course/tree/main/chapters/ro) | [@Sigmoid](https://github.com/SigmoidAI), [@eduard-balamatiuc](https://github.com/eduard-balamatiuc), [@FriptuLudmila](https://github.com/FriptuLudmila), [@tokyo-s](https://github.com/tokyo-s), [@hbkdesign](https://github.com/hbkdesign), [@grumpycatyo-collab](https://github.com/grumpycatyo-collab), [@Angroys](https://github.com/Angroys) |
+
+### Translating the course into your language
+
+As part of our mission to democratise machine learning, we'd love to have the course available in many more languages! Please follow the steps below if you'd like to help translate the course into your language 🙏.
+
+**🗞️ Open an issue**
+
+To get started, navigate to the [_Issues_](https://github.com/huggingface/course/issues) page of this repo and check if anyone else has opened an issue for your language. If not, open a new issue by selecting the _Translation template_ from the _New issue_ button.
+
+Once an issue is created, post a comment to indicate which chapters you'd like to work on and we'll add your name to the list.
+
+**🗣 Join our Discord**
+
+Since it can be difficult to discuss translation details quickly over GitHub issues, we have created dedicated channels for each language on our Discord server. If you'd like to join, follow the instructions at this channel 👉: [https://discord.gg/JfAtkvEtRb](https://discord.gg/JfAtkvEtRb)
+
+**🍴 Fork the repository**
+
+Next, you'll need to [fork this repo](https://docs.github.com/en/get-started/quickstart/fork-a-repo). You can do this by clicking on the **Fork** button on the top-right corner of this repo's page.
+
+Once you've forked the repo, you'll want to get the files on your local machine for editing. You can do that by cloning the fork with Git as follows:
+
+```bash
+git clone https://github.com/YOUR-USERNAME/course
+```
+
+**📋 Copy-paste the English files with a new language code**
+
+The course files are organised under a main directory:
+
+* [`chapters`](https://github.com/huggingface/course/tree/main/chapters): all the text and code snippets associated with the course.
+
+You'll only need to copy the files in the [`chapters/en`](https://github.com/huggingface/course/tree/main/chapters/en) directory, so first navigate to your fork of the repo and run the following:
+
+```bash
+cd ~/path/to/course
+cp -r chapters/en/CHAPTER-NUMBER chapters/LANG-ID/CHAPTER-NUMBER
+```
+
+Here, `CHAPTER-NUMBER` refers to the chapter you'd like to work on and `LANG-ID` should be one of the ISO 639-1 or ISO 639-2 language codes -- see [here](https://www.loc.gov/standards/iso639-2/php/code_list.php) for a handy table.
+
+**✍️ Start translating**
+
+Now comes the fun part - translating the text! The first thing we recommend is translating the part of the `_toctree.yml` file that corresponds to your chapter. This file is used to render the table of contents on the website and provide the links to the Colab notebooks. The only fields you should change are the `title`, ones -- for example, here are the parts of `_toctree.yml` that we'd translate for [Chapter 0](https://huggingface.co/course/chapter0/1?fw=pt):
+
+```yaml
+- title: 0. Setup # Translate this!
+ sections:
+ - local: chapter0/1 # Do not change this!
+ title: Introduction # Translate this!
+```
+
+> 🚨 Make sure the `_toctree.yml` file only contains the sections that have been translated! Otherwise you won't be able to build the content on the website or locally (see below how).
+
+
+Once you have translated the `_toctree.yml` file, you can start translating the [MDX](https://mdxjs.com/) files associated with your chapter.
+
+> 🙋 If the `_toctree.yml` file doesn't yet exist for your language, you can simply create one by copy-pasting from the English version and deleting the sections that aren't related to your chapter. Just make sure it exists in the `chapters/LANG-ID/` directory!
+
+**👷♂️ Build the course locally**
+
+Once you're happy with your changes, you can preview how they'll look by first installing the [`doc-builder`](https://github.com/huggingface/doc-builder) tool that we use for building all documentation at Hugging Face:
+
+```bash
+python -m venv .venv
+source .venv/bin/activate
+python -m pip install --upgrade pip
+python -m pip install "git+https://github.com/huggingface/doc-builder.git"
+```
+
+```bash
+doc-builder preview course ./chapters/LANG-ID --not_python_module
+```
+
+If `LANG-ID` content is partial, make sure `_toctree.yml` only references files that exist in `chapters/LANG-ID`.
+
+If preview starts but all routes return `404`, this might be a local `doc-builder` version mismatch between the Python package and the SvelteKit template:
+
+```bash
+python -m pip uninstall -y hf-doc-builder
+python -m pip install "git+https://github.com/huggingface/doc-builder.git"
+```
+
+Then restart `doc-builder preview`.
+
+> `preview` command does not work with Windows.
+
+This will build and render the course on [http://localhost:5173/](http://localhost:5173/). Although the content looks much nicer on the Hugging Face website, this step will still allow you to check that everything is formatted correctly.
+
+**🚀 Submit a pull request**
+
+If the translations look good locally, the final step is to prepare the content for a pull request. Here, the first think to check is that the files are formatted correctly. For that you can run:
+
+```
+pip install -r requirements.txt
+make style
+```
+
+Once that's run, commit any changes, open a pull request, and tag [@lewtun](https://github.com/lewtun) and [@stevhliu](https://github.com/stevhliu) for a review. If you also know other native-language speakers who are able to review the translation, tag them as well for help. Congratulations, you've now completed your first translation 🥳!
+
+> 🚨 To build the course on the website, double-check your language code exists in `languages` field of the `build_documentation.yml` and `build_pr_documentation.yml` files in the `.github` folder. If not, just add them in their alphabetical order.
+
+## 📔 Jupyter notebooks
+
+The Jupyter notebooks containing all the code from the course are hosted on the [`huggingface/notebooks`](https://github.com/huggingface/notebooks) repo. If you wish to generate them locally, first install the required dependencies:
+
+```bash
+python -m pip install -r requirements.txt
+```
+
+Then run the following script:
+
+```bash
+python utils/generate_notebooks.py --output_dir nbs
+```
+
+This script extracts all the code snippets from the chapters and stores them as notebooks in the `nbs` folder (which is ignored by Git by default).
+
+## ✍️ Contributing a new chapter
+
+> Note: we are not currently accepting community contributions for new chapters. These instructions are for the Hugging Face authors.
+
+Adding a new chapter to the course is quite simple:
+
+1. Create a new directory under `chapters/en/chapterX`, where `chapterX` is the chapter you'd like to add.
+2. Add numbered MDX files `sectionX.mdx` for each section. If you need to include images, place them in the [huggingface-course/documentation-images](https://huggingface.co/datasets/huggingface-course/documentation-images) repository and use the [HTML Images Syntax](https://www.w3schools.com/html/html_images.asp) with the path `https://huggingface.co/datasets/huggingface-course/documentation-images/resolve/main/{langY}/{chapterX}/{your-image.png}`.
+3. Update the `_toctree.yml` file to include your chapter sections -- this information will render the table of contents on the website. If your section involves both the PyTorch and TensorFlow APIs of `transformers`, make sure you include links to both Colabs in the `colab` field.
+
+If you get stuck, check out one of the existing chapters -- this will often show you the expected syntax.
+
+Once you are happy with the content, open a pull request and tag [@lewtun](https://github.com/lewtun) for a review. We recommend adding the first chapter draft as a single pull request -- the team will then provide feedback internally to iterate on the content 🤗!
+
+## Deploying to hf.co/course (for HF staff)
+
+The course content is deployed to [hf.co/course](https://huggingface.co/learn/nlp-course/chapter1/1) by triggering the [GitHub CI](.github/workflows/build_documentation.yml) from the `release` branch. To trigger the build, first create a new branch from `main` that will be used to update the current state on `release`:
+
+```shell
+git checkout main
+git checkout -b bump_release
+```
+
+Next, resolve any conflicts between the `release` and `bump_release` branches. Since this is tiresome to do manually, we can do the following to accept the latest changes:
+
+```shell
+git checkout bump_release
+git merge -s ours release
+```
+
+Next, push the `bump_release` branch and open a PR against `release` (not `main`!). Here is an example [PR](https://github.com/huggingface/course/pull/768). Once the CI is green, merge the PR and this will trigger the GitHub CI to build the new course content. This takes around 10-15 minutes, after which the latest changes will be visible on [hf.co/course](https://huggingface.co/learn/nlp-course/chapter1/1)!
+
+
+## 🙌 Acknowledgements
+
+The structure of this repo and README are inspired by the wonderful [Advanced NLP with spaCy](https://github.com/ines/spacy-course) course.
diff --git a/chapters/ar/_toctree.yml b/chapters/ar/_toctree.yml
index b9e6467fd..93385b8dd 100644
--- a/chapters/ar/_toctree.yml
+++ b/chapters/ar/_toctree.yml
@@ -1,4 +1,4 @@
-- title: 0.الإعداد
- sections:
- - local: chapter0/1
- title: مقدمة
+- title: 0.الإعداد
+ sections:
+ - local: chapter0/1
+ title: مقدمة
diff --git a/chapters/ar/chapter0/1.mdx b/chapters/ar/chapter0/1.mdx
index 8cec15b6e..0dfcd1237 100644
--- a/chapters/ar/chapter0/1.mdx
+++ b/chapters/ar/chapter0/1.mdx
@@ -1,136 +1,136 @@
-
-
-# مقدمة
-
-مرحبًا بك في دورة Hugging Face! ستساعدك هذه المقدمة خلال إعداد بيئة العمل. إذا كنت قد بدأت الدورة للتو، فننصحك أولاً بإلقاء نظرة على [الفصل 1](/course/chapter1)، ثم العودة وإعداد بيئتك حتى تتمكن من تجربة الكود بنفسك.
-
-تتوفر جميع المكتبات التي سنستخدمها في هذه الدورة التدريبية على شكل حزم (Package) Python، لذلك سنوضح لك هنا كيفية إعداد بيئة Python وتثبيت المكتبات المحددة التي ستحتاج إليها.
-
-سنغطي طريقتين لإعداد بيئة العمل الخاصة بك، باستخدام دفتر Colab أو بيئة Python الافتراضية. لا تتردد في اختيار البيئة التي تناسبك أكثر.نوصي المبتدئين بشدة أن يبدأوا باستخدام دفتر Colab.
-
-لاحظ أننا لن نغطي نظام Windows. إذا كنت تعمل على نظام Windows، فإننا نوصي بمتابعة استخدام دفتر Colab. إذا كنت تستخدم توزيعة Linux أو macOS، فيمكنك استخدام أي من الطريقتين الموضحتين هنا.
-
-تعتمد معظم الدورة على امتلاكك لحساب Hugging Face. نوصي بإنشاء حساب الآن: [إنشاء حساب](https://huggingface.co/join).
-
-## استخدام دفتر Google Colab
-
-يعد استخدام دفتر Colab أبسط إعداد ممكن؛ فقط قم بتشغيل دفتر Colab في متصفحك ابدأ مباشرة بالبرمجة!
-
-إذا لم تكن معتادًا على Colab، نوصيك بالبدء باتباع [المقدمة](https://colab.research.google.com/notebooks/intro.ipynb). يتيح لك Colab استخدام بعض أجهزة التسريع، مثل GPUs أو TPUs، وهو مجاني في حال تشغيل مهمات خفيفة.
-
-بمجرد أن تشعر بالأريحية في التنقل في Colab، أنشئ دفتر ملاحظات جديدًا وابدأ في الإعداد:
-
-
-

-
-
-الخطوة التالية هي تثبيت المكتبات التي سنستخدمها في هذه الدورة. سنستخدم `pip` للتثبيت، وهو مدير الحزم لPython. حتى تتمكن من تثبيت مكتبة 🤗 Transformers يمكنك تشغيل أوامر النظام عن طريق تسبقها بالحرف `!` في دفتر Colab, على النحو التالي:
-
-
-
-```
-!pip install transformers
-```
-
-
-يمكنك التأكد من تثبيت الحزمة بشكل صحيح عن طريق استيرادها (import) خلال وقت تشغيل Python:
-
-
-```
-import transformers
-```
-
-
-

-
-
-
-هذا يثبت نسخة خفيفة جدا من مكتبة 🤗 Transformers. أي أنه لم يتم تثبيت أي إطارات عمل محددة للتعلم الآلي (مثل PyTorch أو TensorFlow). نوصي بتثبيت "إصدار التطوير" للمكتبة لأننا سوف نستخدم الكثير من الميزات المختلفة, و هذا الإصدار يأتي مع جميع التبعيات المطلوبة تقريباً لأي حالة استخدام يمكن تخيلها:
-
-
-
-```
-!pip install transformers[sentencepiece]
-```
-
-
-سيستغرق هذا بعض الوقت، لكنك ستكون جاهزًا بعد ذلك لبقية الدورة!
-
-## استخدام بيئة Python افتراضية
-
-إذا كنت تفضل استخدام بيئة Python الافتراضية، فإن الخطوة الأولى هي تثبيت Python على نظامك. للبدء, نوصي باتباع [دليل الإرشادات هذا](https://realpython.com/installing-python/).
-
-بمجرد تثبيت Python، يجب أن تكون قادرًا على تشغيل أوامر Python في الجهاز المستخدم. للتأكد من تثبيته بشكل صحيح قبل المتابعة إلى الخطوات التالية يمكنك البدء بتشغيل الأمر التالي: `python --version`. يجب أن يطبع هذا إصدار Python المتاح الآن على نظامك.
-
-عند تشغيل أمر Python في الجهاز المستخدم، مثل `python --version`، يجب أن تفكر في البرنامج الذي يقوم بتشغيل الأمر الخاص بك باعتباره Python "الرئيسي" على نظامك. نوصي بالحفاظ على هذا التثبيت الرئيسي خاليًا من أي حزم، واستخدامه لإنشاء بيئات منفصلة لكل تطبيق تعمل عليه, وبهذه الطريقة، يمكن لكل تطبيق أن يكون له تبعيات وحزم خاصة به، ولن تقلق بشأن مشكلات التوافق المحتملة مع تطبيقات أخرى.
-
-في Python، يتم ذلك باستخدام [* البيئات الافتراضية *](https://docs.python.org/3/tutorial/venv.html)، وهي عبارة عن تفرعات من المجلدات كل منها قائم بحد ذاته, ويحتوي كل منها على Python مثبت بإصدار معين بالإضافة إلى جميع الحزم التي يحتاجها التطبيق. يمكن إنشاء مثل هذه البيئة الافتراضية باستخدام عدد من الأدوات المختلفة ، لكننا سنستخدم حزمة Python الرسمية لهذا الغرض، والتي تسمى [`venv`](https://docs.python.org/3/library/venv.html#module-venv).
-
-أولاً، قم بإنشاء المجلد الذي تريد أن يتواجد فيه التطبيق الخاص بك -على سبيل المثال، قد ترغب في إنشاء مجلد جديد يسمى *transformers-course* في المجلد الرئيسي للدورة:
-
-
-```
-mkdir ~/transformers-course
-cd ~/transformers-course
-```
-
-
-
-من داخل هذا المجلد، أنشئ بيئة افتراضية باستخدام وحدة Python `venv`:
-
-
-
-```
-python -m venv .env
-```
-
-
-يجب أن يكون لديك الآن مجلد يسمى *.env* في المجلد الفارغ الخاص بك:
-
-
-```
-ls -a
-```
-
-```out
-. .. .env
-```
-
-
-يمكنك الدخول والخروج من بيئتك الافتراضية باستخدام أوامر "التنشيط" و "إلغاء التنشيط":
-
-
-```
-# Activate the virtual environment
-source .env/bin/activate
-
-# Deactivate the virtual environment
-deactivate
-```
-
-
-يمكنك التأكد من تنشيط البيئة عن طريق تشغيل الأمر `which python`: إذا كان يشير إلى البيئة الافتراضية، فقد قمت بتنشيطها بنجاح!
-
-
-```
-which python
-```
-
-```out
-/home//transformers-course/.env/bin/python
-```
-
-
-
-### تثبيت التبعيات
-
-كما في القسم السابق حول استخدام مثيلات Google Colab، ستحتاج الآن إلى تثبيت الحزم المطلوبة للمتابعة. مرة أخرى، يمكنك تثبيت إصدار التطوير من 🤗 Transformers باستخدام مدير الحزم `pip`:
-
-
-```
-pip install "transformers[sentencepiece]"
-```
-
-
-أنت الآن جاهز تمامًا للانطلاق!
-
+
+
+# مقدمة
+
+مرحبًا بك في دورة Hugging Face! ستساعدك هذه المقدمة خلال إعداد بيئة العمل. إذا كنت قد بدأت الدورة للتو، فننصحك أولاً بإلقاء نظرة على [الفصل 1](/course/chapter1)، ثم العودة وإعداد بيئتك حتى تتمكن من تجربة الكود بنفسك.
+
+تتوفر جميع المكتبات التي سنستخدمها في هذه الدورة التدريبية على شكل حزم (Package) Python، لذلك سنوضح لك هنا كيفية إعداد بيئة Python وتثبيت المكتبات المحددة التي ستحتاج إليها.
+
+سنغطي طريقتين لإعداد بيئة العمل الخاصة بك، باستخدام دفتر Colab أو بيئة Python الافتراضية. لا تتردد في اختيار البيئة التي تناسبك أكثر.نوصي المبتدئين بشدة أن يبدأوا باستخدام دفتر Colab.
+
+لاحظ أننا لن نغطي نظام Windows. إذا كنت تعمل على نظام Windows، فإننا نوصي بمتابعة استخدام دفتر Colab. إذا كنت تستخدم توزيعة Linux أو macOS، فيمكنك استخدام أي من الطريقتين الموضحتين هنا.
+
+تعتمد معظم الدورة على امتلاكك لحساب Hugging Face. نوصي بإنشاء حساب الآن: [إنشاء حساب](https://huggingface.co/join).
+
+## استخدام دفتر Google Colab
+
+يعد استخدام دفتر Colab أبسط إعداد ممكن؛ فقط قم بتشغيل دفتر Colab في متصفحك ابدأ مباشرة بالبرمجة!
+
+إذا لم تكن معتادًا على Colab، نوصيك بالبدء باتباع [المقدمة](https://colab.research.google.com/notebooks/intro.ipynb). يتيح لك Colab استخدام بعض أجهزة التسريع، مثل GPUs أو TPUs، وهو مجاني في حال تشغيل مهمات خفيفة.
+
+بمجرد أن تشعر بالأريحية في التنقل في Colab، أنشئ دفتر ملاحظات جديدًا وابدأ في الإعداد:
+
+
+

+
+
+الخطوة التالية هي تثبيت المكتبات التي سنستخدمها في هذه الدورة. سنستخدم `pip` للتثبيت، وهو مدير الحزم لPython. حتى تتمكن من تثبيت مكتبة 🤗 Transformers يمكنك تشغيل أوامر النظام عن طريق تسبقها بالحرف `!` في دفتر Colab, على النحو التالي:
+
+
+
+```
+!pip install transformers
+```
+
+
+يمكنك التأكد من تثبيت الحزمة بشكل صحيح عن طريق استيرادها (import) خلال وقت تشغيل Python:
+
+
+```
+import transformers
+```
+
+
+

+
+
+
+هذا يثبت نسخة خفيفة جدا من مكتبة 🤗 Transformers. أي أنه لم يتم تثبيت أي إطارات عمل محددة للتعلم الآلي (مثل PyTorch أو TensorFlow). نوصي بتثبيت "إصدار التطوير" للمكتبة لأننا سوف نستخدم الكثير من الميزات المختلفة, و هذا الإصدار يأتي مع جميع التبعيات المطلوبة تقريباً لأي حالة استخدام يمكن تخيلها:
+
+
+
+```
+!pip install transformers[sentencepiece]
+```
+
+
+سيستغرق هذا بعض الوقت، لكنك ستكون جاهزًا بعد ذلك لبقية الدورة!
+
+## استخدام بيئة Python افتراضية
+
+إذا كنت تفضل استخدام بيئة Python الافتراضية، فإن الخطوة الأولى هي تثبيت Python على نظامك. للبدء, نوصي باتباع [دليل الإرشادات هذا](https://realpython.com/installing-python/).
+
+بمجرد تثبيت Python، يجب أن تكون قادرًا على تشغيل أوامر Python في الجهاز المستخدم. للتأكد من تثبيته بشكل صحيح قبل المتابعة إلى الخطوات التالية يمكنك البدء بتشغيل الأمر التالي: `python --version`. يجب أن يطبع هذا إصدار Python المتاح الآن على نظامك.
+
+عند تشغيل أمر Python في الجهاز المستخدم، مثل `python --version`، يجب أن تفكر في البرنامج الذي يقوم بتشغيل الأمر الخاص بك باعتباره Python "الرئيسي" على نظامك. نوصي بالحفاظ على هذا التثبيت الرئيسي خاليًا من أي حزم، واستخدامه لإنشاء بيئات منفصلة لكل تطبيق تعمل عليه, وبهذه الطريقة، يمكن لكل تطبيق أن يكون له تبعيات وحزم خاصة به، ولن تقلق بشأن مشكلات التوافق المحتملة مع تطبيقات أخرى.
+
+في Python، يتم ذلك باستخدام [* البيئات الافتراضية *](https://docs.python.org/3/tutorial/venv.html)، وهي عبارة عن تفرعات من المجلدات كل منها قائم بحد ذاته, ويحتوي كل منها على Python مثبت بإصدار معين بالإضافة إلى جميع الحزم التي يحتاجها التطبيق. يمكن إنشاء مثل هذه البيئة الافتراضية باستخدام عدد من الأدوات المختلفة ، لكننا سنستخدم حزمة Python الرسمية لهذا الغرض، والتي تسمى [`venv`](https://docs.python.org/3/library/venv.html#module-venv).
+
+أولاً، قم بإنشاء المجلد الذي تريد أن يتواجد فيه التطبيق الخاص بك -على سبيل المثال، قد ترغب في إنشاء مجلد جديد يسمى *transformers-course* في المجلد الرئيسي للدورة:
+
+
+```
+mkdir ~/transformers-course
+cd ~/transformers-course
+```
+
+
+
+من داخل هذا المجلد، أنشئ بيئة افتراضية باستخدام وحدة Python `venv`:
+
+
+
+```
+python -m venv .env
+```
+
+
+يجب أن يكون لديك الآن مجلد يسمى *.env* في المجلد الفارغ الخاص بك:
+
+
+```
+ls -a
+```
+
+```out
+. .. .env
+```
+
+
+يمكنك الدخول والخروج من بيئتك الافتراضية باستخدام أوامر "التنشيط" و "إلغاء التنشيط":
+
+
+```
+# Activate the virtual environment
+source .env/bin/activate
+
+# Deactivate the virtual environment
+deactivate
+```
+
+
+يمكنك التأكد من تنشيط البيئة عن طريق تشغيل الأمر `which python`: إذا كان يشير إلى البيئة الافتراضية، فقد قمت بتنشيطها بنجاح!
+
+
+```
+which python
+```
+
+```out
+/home//transformers-course/.env/bin/python
+```
+
+
+
+### تثبيت التبعيات
+
+كما في القسم السابق حول استخدام مثيلات Google Colab، ستحتاج الآن إلى تثبيت الحزم المطلوبة للمتابعة. مرة أخرى، يمكنك تثبيت إصدار التطوير من 🤗 Transformers باستخدام مدير الحزم `pip`:
+
+
+```
+pip install "transformers[sentencepiece]"
+```
+
+
+أنت الآن جاهز تمامًا للانطلاق!
+
diff --git a/chapters/bn/_toctree.yml b/chapters/bn/_toctree.yml
index 30d73183a..18c2d0020 100644
--- a/chapters/bn/_toctree.yml
+++ b/chapters/bn/_toctree.yml
@@ -1,14 +1,14 @@
-- title: 0. সেটআপ
- sections:
- - local: chapter0/1
- title: ভূমিকা
-
-- title: 1. ট্রান্সফরমার মডেল
- sections:
- - local: chapter1/1
- title: ভূমিকা
-
-- title: 2. 🤗Transformers এর ব্যবহার
- sections:
- - local: chapter2/1
+- title: 0. সেটআপ
+ sections:
+ - local: chapter0/1
+ title: ভূমিকা
+
+- title: 1. ট্রান্সফরমার মডেল
+ sections:
+ - local: chapter1/1
+ title: ভূমিকা
+
+- title: 2. 🤗Transformers এর ব্যবহার
+ sections:
+ - local: chapter2/1
title: ভূমিকা
\ No newline at end of file
diff --git a/chapters/bn/chapter0/1.mdx b/chapters/bn/chapter0/1.mdx
index 9235ad8a3..c2ab18dff 100644
--- a/chapters/bn/chapter0/1.mdx
+++ b/chapters/bn/chapter0/1.mdx
@@ -1,112 +1,112 @@
-# ভূমিকা
-
-হাগিং ফেস কোর্সে স্বাগতম! এই অধ্যায়টি একটি "ওয়ার্কিং এনভায়রনমেন্ট" সেট আপ করতে আপনাকে গাইড করবে। আপনি যদি এইপ্রথম কোর্সটি শুরু করে থাকেন, আমরা আপনাকে প্রথমে [অধ্যায় 1](/course/chapter1) একবার দেখে পড়ে আসার পরামর্শ দিচ্ছি, ফিরে এসে "ওয়ার্কিং এনভায়রনমেন্ট" সেট আপ করুন যাতে আপনি নিজেই কোডটি চেষ্টা করতে পারেন।
-
-এই কোর্সে আমরা যে সমস্ত লাইব্রেরিগুলি ব্যবহার করব সেগুলি পাইথন প্যাকেজ হিসাবে পাওয়া যাবে, তাই এখানে আমরা আপনাকে দেখাব কিভাবে একটি পাইথন এনভায়রনমেন্ট সেট আপ করতে হয় এবং আপনার প্রয়োজনীয় নির্দিষ্ট লাইব্রেরিগুলি ইনস্টল করতে হয়৷
-
-Colab নোটবুক বা পাইথন virtual environment ব্যবহার করে আমরা "ওয়ার্কিং এনভায়রনমেন্ট" সেট-আপ করার দুটি উপায় কভার করব। যে পদ্ধতিটি আপনার কাছে সহজ সেটি আপনি বেছে নিতে পাড়েন। যারা নতুন শুরু করছেন তাদের জন্য আমরা Colab নোটবুক ব্যবহার করে শুরু করতে জোরালোভাবে রিকমেন্ড করি।
-
-মনে রাখবেন যে এখানে উইন্ডোজ সিস্টেম কভার করা হবে না। আপনি যদি উইন্ডোজ চালান, তাহলে আমরা Colab নোটবুক ব্যবহার করে ফলো করার পরামর্শ দিচ্ছি। আর আপনি যদি লিনাক্স ডিস্ট্রিবিউশন বা ম্যাকওএস ব্যবহার করেন তবে এখানে বর্ণিত পদ্ধতির যেকোনো একটি ব্যবহার করতে পারেন।
-
-কোর্সের অনেকটাই হাগিং ফেস অ্যাকাউন্ট উপর নির্ভর করবে। তাই আমরা একটি একাউন্ট ওপেন করার করার পরামর্শ দিচ্ছি: [একটি অ্যাকাউন্ট তৈরি করুন](https://huggingface.co/join)।
-
-## Google Colab নোটবুক ব্যবহার করার পদ্ধতি
-
-Colab নোটবুক ব্যবহার করার সবচেয়ে সহজ সেটআপ হচ্ছে ব্রাউজারে একটি নোটবুক ওপেন করুন এবং সরাসরি কোডিং এ যান!
-
-আপনি যদি Colab-এর সাথে পরিচিত না হন তাহলে আমরা আপনাকে [Colab পরিচয়](https://colab.research.google.com/notebooks/intro.ipynb) অনুসরণ করে শুরু করার পরামর্শ দিচ্ছি। Colab আপনাকে কিছু এক্সেলারেসন হার্ডওয়্যার ব্যবহার করতে দেয়, যেমন GPUs বা TPUs যা ছোট ওয়ার্ক লোডের জন্য ফ্রি।
-
-Colab-এর উপর আপানার হাত চলে আসলে একটি নতুন নোটবুক ওপেন করে সেট-আপ শুরু করুন:
-
-
-

-
-
-পরবর্তী ধাপে আমরা এই কোর্সে ব্যবহার হবে এমন লাইব্রেরিগুলি ইনস্টল করা দেখাবো। আমরা ইনস্টলেশনের জন্য পাইথনের প্যাকেজ ম্যানেজার `pip` ব্যবহার করব। নোটবুকগুলিতে, আপনি `!` অক্ষর দিয়ে আগে সিস্টেম কমান্ড চালাতে পারবেন। যেমন ধরুন, নিচের কমান্ডটি দিয়ে 🤗 Transformers লাইব্রেরি ইনস্টল করতে পারবেন:
-
-```
-!pip install transformers
-```
-
-প্যাকেজটি আপনার পাইথন রানটাইমের মধ্যে সঠিকভাবে ইনস্টল করা হয়েছে কিনা তা import করে নিশ্চিত হতে পাড়েন।
-
-```
-import transformers
-```
-
-
-

-
-
-এটি 🤗 ট্রান্সফরমারের একটি খুব লাইট ভার্সন ইনস্টল করে। বিশেষ করে, যদিনা নির্দিষ্ট মেশিন লার্নিং ফ্রেমওয়ার্ক (যেমন PyTorch বা TensorFlow) ইনস্টল করা থাকে। যেহেতু আমরা লাইব্রেরির বিভিন্ন ফিচার ব্যবহার করব, তাই আমরা ডেভেলপমেন্ট ভার্সন ইনস্টল করার পরামর্শ দিচ্ছি, যতে ধারানা করার এমন সব ইউজ কেসে কাজ করবে:
-
-```
-!pip install transformers[sentencepiece]
-```
-
-ইনস্টল হতে কিছুটা সময় লাগবে, কিন্তু এরপর আপনি বাকি কোর্সের জন্য প্রস্তুত হয়ে যাবেন!
-
-## একটি পাইথন virtual environment ব্যবহার করা
-
-আপনি যদি পাইথন virtual environment ব্যবহার করতে পছন্দ করেন, প্রথম ধাপ হল আপনার সিস্টেমে পাইথন ইনস্টল করা। শুরু করার জন্য আমরা [এই নির্দেশিকা](https://realpython.com/installing-python/) অনুসরণ করার পরামর্শ দিচ্ছি।
-
-একবার আপনি পাইথন ইনস্টল করলে, আপনি আপনার টার্মিনালে পাইথন কমান্ড চালাতে সক্ষম হবেন। পরবর্তী ধাপে যাওয়ার আগে এটি সঠিকভাবে ইনস্টল করা হয়েছে তা নিশ্চিত করতে আপনি নিম্নলিখিত কমান্ডটি চালিয়ে শুরু করতে পারেন: `python --version`। এটি আপনার সিস্টেমে ইনস্টল হওয়া পাইথন সংস্করণটি প্রিন্ট করা উচিত।
-
-আপনার টার্মিনালে পাইথন কমান্ড চালানোর সময়, যেমন `python --version`, আপানাকে ভাবতে হবে যে এটি "main" পাইথন প্রোগ্রাম যা আপানার কমান্ড টিকে রান করছে। আমরা এই মূল ইনস্টলেশনটিকে যেকোন প্যাকেজ ইনস্টল থেকে মুক্ত রাখার সুপারিশ করি। এ আপনি যখন আলাদা অ্যাপ্লিকেশনে কাজ করবেন তখন তার জন্য আলাদা virtual environment তৈরি করতে এই পাইথন ইনস্টলেশনটিকে ব্যবহার করবেন। এতে করে প্রতিটি অ্যাপ্লিকেশনের নিজস্ব ডিপেন্ডেন্সি এবং প্যাকেজ আলাদা থাকবে এবং অন্যান্য অ্যাপ্লিকেশনের সাথে এর সম্ভাব্য কম্পাটিবিলটি নিয়ে আপানকে সমস্যায় করতে হবে না।
-
-পাইথনে এটি [*virtual environments*](https://docs.python.org/3/tutorial/venv.html) দিয়ে করা হয়, যেটি স্বয়ংসম্পূর্ণ ডিরেক্টরি ট্রি। যার প্রত্যেকটিতে এপ্লিকেশনের প্রয়োজনীয় সমস্ত প্যাকেজের পাশাপাশি একটি নির্দিষ্ট পাইথন ভার্শনের পাইথন ইনস্টলেশন আছে। এই ধরনের একটি virtual environments বিভিন্ন ভাবে তৈরি করা যেতে পারে। তবে আমরা এর জন্য অফিসিয়াল পাইথন প্যাকেজ ব্যবহার করব, যাকে বলা হয় [`venv`](https://docs.python.org/3/library) /venv.html#module-venv)।
-
-প্রথমে, আপনি যে ডিরেক্টরিটি আপনার অ্যাপ্লিকেশনটিতে রাখতে চান তা তৈরি করুন — উদাহরণস্বরূপ, আপনি আপনার হোম ডিরেক্টরির বা ফোল্ডার ভেতর *transformers-course* নামে একটি নতুন ডিরেক্টরি তৈরি করতে চাইতে পারেন:
-
-```
-mkdir ~/transformers-course
-cd ~/transformers-course
-```
-
-এই ডিরেক্টরির ভিতর থেকে, পাইথন `venv` মডিউল ব্যবহার করে একটি virtual environment তৈরি করুন:
-
-```
-python -m venv .env
-```
-
-আপনার এখন *.env* নামে একটি ফোল্ডার থাকা উচিত, অন্যথায় খালি ফোল্ডার :
-
-```
-ls -a
-```
-
-```out
-. .. .env
-```
-
-
-আপনি এখন virtual environment টি `activate` করতে বা `deactivate` নিচের কমান্ড গুলো ব্যবহার করতে পারেন।
-
-```
-# virtual environment টি activate করার কমান্ড
-source .env/bin/activate
-
-# virtual environment টি deactivate করার কমান্ড
-deactivate
-```
-
-`which python` কমান্ড চালিয়ে নিশ্চিত করতে পারেন যে virtual environment টি activate হয়েছে কিনা।
-যদি এটি virtual environment টি কে পয়েন্ট করে করে, তাহলে আপনি সফলভাবে এটি সক্রিয় করেছেন!
-
-```
-which python
-```
-
-```out
-/home//transformers-course/.env/bin/python
-```
-
-### ডিপেন্ডেন্সি ইনস্টল করা
-
-আগের সেকশনে Google Colab এ যেভাবে প্যাকেজ ইনস্টল করা হয়েছে একই ভাবে এখানেও `pip` প্যাকেজ ম্যানেজার ব্যবহার করে 🤗 Transformer এর development সংস্করণ ইনস্টল করতে পারেন:
-
-````
-pip install "transformers[sentencepiece]"
-````
-
-আপনি এখন শুরু করা জন্য সম্পূর্ণ প্রস্তুত!
+# ভূমিকা
+
+হাগিং ফেস কোর্সে স্বাগতম! এই অধ্যায়টি একটি "ওয়ার্কিং এনভায়রনমেন্ট" সেট আপ করতে আপনাকে গাইড করবে। আপনি যদি এইপ্রথম কোর্সটি শুরু করে থাকেন, আমরা আপনাকে প্রথমে [অধ্যায় 1](/course/chapter1) একবার দেখে পড়ে আসার পরামর্শ দিচ্ছি, ফিরে এসে "ওয়ার্কিং এনভায়রনমেন্ট" সেট আপ করুন যাতে আপনি নিজেই কোডটি চেষ্টা করতে পারেন।
+
+এই কোর্সে আমরা যে সমস্ত লাইব্রেরিগুলি ব্যবহার করব সেগুলি পাইথন প্যাকেজ হিসাবে পাওয়া যাবে, তাই এখানে আমরা আপনাকে দেখাব কিভাবে একটি পাইথন এনভায়রনমেন্ট সেট আপ করতে হয় এবং আপনার প্রয়োজনীয় নির্দিষ্ট লাইব্রেরিগুলি ইনস্টল করতে হয়৷
+
+Colab নোটবুক বা পাইথন virtual environment ব্যবহার করে আমরা "ওয়ার্কিং এনভায়রনমেন্ট" সেট-আপ করার দুটি উপায় কভার করব। যে পদ্ধতিটি আপনার কাছে সহজ সেটি আপনি বেছে নিতে পাড়েন। যারা নতুন শুরু করছেন তাদের জন্য আমরা Colab নোটবুক ব্যবহার করে শুরু করতে জোরালোভাবে রিকমেন্ড করি।
+
+মনে রাখবেন যে এখানে উইন্ডোজ সিস্টেম কভার করা হবে না। আপনি যদি উইন্ডোজ চালান, তাহলে আমরা Colab নোটবুক ব্যবহার করে ফলো করার পরামর্শ দিচ্ছি। আর আপনি যদি লিনাক্স ডিস্ট্রিবিউশন বা ম্যাকওএস ব্যবহার করেন তবে এখানে বর্ণিত পদ্ধতির যেকোনো একটি ব্যবহার করতে পারেন।
+
+কোর্সের অনেকটাই হাগিং ফেস অ্যাকাউন্ট উপর নির্ভর করবে। তাই আমরা একটি একাউন্ট ওপেন করার করার পরামর্শ দিচ্ছি: [একটি অ্যাকাউন্ট তৈরি করুন](https://huggingface.co/join)।
+
+## Google Colab নোটবুক ব্যবহার করার পদ্ধতি
+
+Colab নোটবুক ব্যবহার করার সবচেয়ে সহজ সেটআপ হচ্ছে ব্রাউজারে একটি নোটবুক ওপেন করুন এবং সরাসরি কোডিং এ যান!
+
+আপনি যদি Colab-এর সাথে পরিচিত না হন তাহলে আমরা আপনাকে [Colab পরিচয়](https://colab.research.google.com/notebooks/intro.ipynb) অনুসরণ করে শুরু করার পরামর্শ দিচ্ছি। Colab আপনাকে কিছু এক্সেলারেসন হার্ডওয়্যার ব্যবহার করতে দেয়, যেমন GPUs বা TPUs যা ছোট ওয়ার্ক লোডের জন্য ফ্রি।
+
+Colab-এর উপর আপানার হাত চলে আসলে একটি নতুন নোটবুক ওপেন করে সেট-আপ শুরু করুন:
+
+
+

+
+
+পরবর্তী ধাপে আমরা এই কোর্সে ব্যবহার হবে এমন লাইব্রেরিগুলি ইনস্টল করা দেখাবো। আমরা ইনস্টলেশনের জন্য পাইথনের প্যাকেজ ম্যানেজার `pip` ব্যবহার করব। নোটবুকগুলিতে, আপনি `!` অক্ষর দিয়ে আগে সিস্টেম কমান্ড চালাতে পারবেন। যেমন ধরুন, নিচের কমান্ডটি দিয়ে 🤗 Transformers লাইব্রেরি ইনস্টল করতে পারবেন:
+
+```
+!pip install transformers
+```
+
+প্যাকেজটি আপনার পাইথন রানটাইমের মধ্যে সঠিকভাবে ইনস্টল করা হয়েছে কিনা তা import করে নিশ্চিত হতে পাড়েন।
+
+```
+import transformers
+```
+
+
+

+
+
+এটি 🤗 ট্রান্সফরমারের একটি খুব লাইট ভার্সন ইনস্টল করে। বিশেষ করে, যদিনা নির্দিষ্ট মেশিন লার্নিং ফ্রেমওয়ার্ক (যেমন PyTorch বা TensorFlow) ইনস্টল করা থাকে। যেহেতু আমরা লাইব্রেরির বিভিন্ন ফিচার ব্যবহার করব, তাই আমরা ডেভেলপমেন্ট ভার্সন ইনস্টল করার পরামর্শ দিচ্ছি, যতে ধারানা করার এমন সব ইউজ কেসে কাজ করবে:
+
+```
+!pip install transformers[sentencepiece]
+```
+
+ইনস্টল হতে কিছুটা সময় লাগবে, কিন্তু এরপর আপনি বাকি কোর্সের জন্য প্রস্তুত হয়ে যাবেন!
+
+## একটি পাইথন virtual environment ব্যবহার করা
+
+আপনি যদি পাইথন virtual environment ব্যবহার করতে পছন্দ করেন, প্রথম ধাপ হল আপনার সিস্টেমে পাইথন ইনস্টল করা। শুরু করার জন্য আমরা [এই নির্দেশিকা](https://realpython.com/installing-python/) অনুসরণ করার পরামর্শ দিচ্ছি।
+
+একবার আপনি পাইথন ইনস্টল করলে, আপনি আপনার টার্মিনালে পাইথন কমান্ড চালাতে সক্ষম হবেন। পরবর্তী ধাপে যাওয়ার আগে এটি সঠিকভাবে ইনস্টল করা হয়েছে তা নিশ্চিত করতে আপনি নিম্নলিখিত কমান্ডটি চালিয়ে শুরু করতে পারেন: `python --version`। এটি আপনার সিস্টেমে ইনস্টল হওয়া পাইথন সংস্করণটি প্রিন্ট করা উচিত।
+
+আপনার টার্মিনালে পাইথন কমান্ড চালানোর সময়, যেমন `python --version`, আপানাকে ভাবতে হবে যে এটি "main" পাইথন প্রোগ্রাম যা আপানার কমান্ড টিকে রান করছে। আমরা এই মূল ইনস্টলেশনটিকে যেকোন প্যাকেজ ইনস্টল থেকে মুক্ত রাখার সুপারিশ করি। এ আপনি যখন আলাদা অ্যাপ্লিকেশনে কাজ করবেন তখন তার জন্য আলাদা virtual environment তৈরি করতে এই পাইথন ইনস্টলেশনটিকে ব্যবহার করবেন। এতে করে প্রতিটি অ্যাপ্লিকেশনের নিজস্ব ডিপেন্ডেন্সি এবং প্যাকেজ আলাদা থাকবে এবং অন্যান্য অ্যাপ্লিকেশনের সাথে এর সম্ভাব্য কম্পাটিবিলটি নিয়ে আপানকে সমস্যায় করতে হবে না।
+
+পাইথনে এটি [*virtual environments*](https://docs.python.org/3/tutorial/venv.html) দিয়ে করা হয়, যেটি স্বয়ংসম্পূর্ণ ডিরেক্টরি ট্রি। যার প্রত্যেকটিতে এপ্লিকেশনের প্রয়োজনীয় সমস্ত প্যাকেজের পাশাপাশি একটি নির্দিষ্ট পাইথন ভার্শনের পাইথন ইনস্টলেশন আছে। এই ধরনের একটি virtual environments বিভিন্ন ভাবে তৈরি করা যেতে পারে। তবে আমরা এর জন্য অফিসিয়াল পাইথন প্যাকেজ ব্যবহার করব, যাকে বলা হয় [`venv`](https://docs.python.org/3/library) /venv.html#module-venv)।
+
+প্রথমে, আপনি যে ডিরেক্টরিটি আপনার অ্যাপ্লিকেশনটিতে রাখতে চান তা তৈরি করুন — উদাহরণস্বরূপ, আপনি আপনার হোম ডিরেক্টরির বা ফোল্ডার ভেতর *transformers-course* নামে একটি নতুন ডিরেক্টরি তৈরি করতে চাইতে পারেন:
+
+```
+mkdir ~/transformers-course
+cd ~/transformers-course
+```
+
+এই ডিরেক্টরির ভিতর থেকে, পাইথন `venv` মডিউল ব্যবহার করে একটি virtual environment তৈরি করুন:
+
+```
+python -m venv .env
+```
+
+আপনার এখন *.env* নামে একটি ফোল্ডার থাকা উচিত, অন্যথায় খালি ফোল্ডার :
+
+```
+ls -a
+```
+
+```out
+. .. .env
+```
+
+
+আপনি এখন virtual environment টি `activate` করতে বা `deactivate` নিচের কমান্ড গুলো ব্যবহার করতে পারেন।
+
+```
+# virtual environment টি activate করার কমান্ড
+source .env/bin/activate
+
+# virtual environment টি deactivate করার কমান্ড
+deactivate
+```
+
+`which python` কমান্ড চালিয়ে নিশ্চিত করতে পারেন যে virtual environment টি activate হয়েছে কিনা।
+যদি এটি virtual environment টি কে পয়েন্ট করে করে, তাহলে আপনি সফলভাবে এটি সক্রিয় করেছেন!
+
+```
+which python
+```
+
+```out
+/home//transformers-course/.env/bin/python
+```
+
+### ডিপেন্ডেন্সি ইনস্টল করা
+
+আগের সেকশনে Google Colab এ যেভাবে প্যাকেজ ইনস্টল করা হয়েছে একই ভাবে এখানেও `pip` প্যাকেজ ম্যানেজার ব্যবহার করে 🤗 Transformer এর development সংস্করণ ইনস্টল করতে পারেন:
+
+````
+pip install "transformers[sentencepiece]"
+````
+
+আপনি এখন শুরু করা জন্য সম্পূর্ণ প্রস্তুত!
diff --git a/chapters/bn/chapter1/1.mdx b/chapters/bn/chapter1/1.mdx
index c3c0d7682..6f8feec49 100644
--- a/chapters/bn/chapter1/1.mdx
+++ b/chapters/bn/chapter1/1.mdx
@@ -1,64 +1,64 @@
-# ভূমিকা
-
-
-
-## 🤗 কোর্সে স্বাগতম!
-
-
-
-এই কোর্সটি আপনাকে [হাগিং ফেস](https://huggingface.co/) ইকোসিস্টেম থেকে — 🤗
- [🤗 ট্রান্সফরমার](https://github.com/huggingface/transformers),[🤗 ডেটাসেট](https://github.com/huggingface/datasets), [🤗 টোকেনাইজার](https://github.com/huggingface/tokenizers),এবং [🤗 অ্যাক্সিলারেট](https://github.com/huggingface/accelerate) — সেইসাথে হাগিং ফেস হাব থেকে লাইব্রেরি ব্যবহার করে ন্যচারাল ল্যঙ্গুএজ প্রসেসিং(NLP) শেখাবে। এটি সাইট টি বিজ্ঞাপন ছাড়াই সম্পূর্ণ ফ্রি।
-
-
-## এই কোর্সটি থেকে কি আশা করা যায়?
-
-এখানে কোর্সের একটি সংক্ষিপ্ত বিবরণ রয়েছে:
-
-
-

-

-
-
-- অধ্যায় ১ থেকে ৪ 🤗 ট্রান্সফরমার লাইব্রেরির মূল ধারণাগুলির একটি ভূমিকা প্রদান করে। কোর্সের এই অংশের শেষে, আপনি ট্রান্সফরমার মডেলগুলি কীভাবে কাজ করে তার সাথে পরিচিত হবেন। এছাড়াও [হাগিং ফেস হাব](https://huggingface.co/models) থেকে একটি মডেল কীভাবে ব্যবহার করতে হয়, কীভাবে মডেল এর ডেটাসেটিকে ফাইন-টিউন করতে হয় এবং হাবে কীভাবে আপনার ফলাফল শেয়ার করতে হয় তা জানতে পারবেন!
-
-- ক্লাসিক NLP টাস্কগুলোর গভীরে যাওয়ার আগে অধ্যায় ৫ থেকে ৮, আপনাকে 🤗 ডেটাসেট এবং 🤗 টোকেনাইজারগুলির মূল বিষয়গুলি শেখাবে৷ এই অংশের শেষে, আপনি নিজেই সবচেয়ে কমন NLP সমস্যাগুলি সমাধান করতে পাড়বেন।
-
-- অধ্যায় ৯ থেকে ১২, এবং স্পীচ প্রসেসিং এবং কম্পিউটার ভিশনের কাজগুলো ট্রান্সফরমার মডেলগুলোকে কীভাবে ব্যবহার করা যেতে পারে তা খতিয়ে দেখে। অধ্যায়টি পড়তে পড়তে আপনি শিখবেন কিভাবে আপনার মডেল বিল্ড এবং ডেমো শেয়ার করতে হয়, কিভাবে প্রডাকশন এনভায়রনমেন্টের জন্য অপ্টিমাইজ করতে হয়। এই অংশের শেষ নাগাদ, (প্রায়) যেকোনও মেশিন লার্নিং সমস্যায় আপনি 🤗 ট্রান্সফরমার প্রয়োগ করতে প্রস্তুত হয়ে যাবেন!
-
-
-এই কোর্স::
-
-* পাইথন সম্পর্কে ভাল জ্ঞান প্রয়োজন
-* একটি প্রাথমিক ডিপ লার্নিং কোর্স করে নেওয়া ভালো, যেমন fast.ai-এর [fast.ai's](https://www.fast.ai/) [Practical Deep Learning for Coders](https://course.fast.ai/) বা [DeepLearning.AI](https://www.deeplearning.ai/) এর প্রোগ্রামগুলির মধ্যে একটি।
-* [PyTorch](https://pytorch.org/) বা [TensorFlow](https://www.tensorflow.org/) জানা জরুরি না, যদিও এদের যেকোনো একটির সাথে কিছু পরিচয় থাকলে সেটা আপানাকে সাহায্য করবে।
-
-আপনি এই কোর্সটি সম্পন্ন করার পরে, আমরা DeepLearning.AI-এর [Natural Language Processing Specialization](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh) কোর্সটি করার পরামর্শ দিই, যেটি প্রথাগত NLP মডেল যেমন naive Bayes এবং LSTMs সম্পর্কে জানতে আপনাকে সাহায্য করবে!
-
-## আমরা কারা??
-
-লেখক সম্পর্কে::
-
-**Matthew Carrigan ম্যাথিউ ক্যারিগান** Hugging Face এর একজন মেশিন লার্নিং ইঞ্জিনিয়ার। তিনি আয়ারল্যান্ডের ডাবলিনে থাকেন এবং পূর্বে Parse.ly-এ একজন ML ইঞ্জিনিয়ার হিসেবে এবং তার আগে ট্রিনিটি কলেজ ডাবলিন-এ একজন পোস্ট-ডক্টরাল গবেষক হিসেবে কাজ করেছেন। তিনি বিশ্বাস করেন না যে আমরা বিদ্যমান আর্কিটেকচারগুলিকে স্কেল করে AGI তে পৌছাবো, তবে তিনি দৃড়ভাবে আশা করেন যে আমারা রোবট অমরত্বের দিকে যাচ্ছি৷
-
-**Lysandre Debut লিসান্দ্রে ডেব্যু** Hugging Face এর একজন মেশিন লার্নিং ইঞ্জিনিয়ার এবং খুব প্রাথমিক পর্যায় থেকে 🤗 Transformers লাইব্রেরিতে কাজ করছেন। তার লক্ষ্য হল একটি খুব সাধারণ API-এর সাহায্যে টুল ডেভেলপ করে সবার জন্য NLP কে সুবোধ্য করে তোলা।
-
-**Sylvain Gugger সিলভাইন গুগার** হলেন হাগিং ফেসের একজন গবেষণা প্রকৌশলী এবং 🤗 ট্রান্সফরমার লাইব্রেরির মূল রক্ষণাবেক্ষণকারীদের একজন। পূর্বে তিনি fast.ai-এর একজন গবেষণা বিজ্ঞানী ছিলেন এবং জেরেমি হাওয়ার্ডের সাথে _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ বইটি লেখেন। তার গবেষণার মূল ফোকাস হল মডেল গুলিকে আরও উন্নত করে এবং অল্প রিসোর্স ব্যবহার করে ট্রেনিং ব্যবস্থা করার মাধ্যমে সবার জন্য deep learning এর কৌশলগুলি আরও সুবোধ্য করে তোলা।
-
-**Merve Noyan মার্ভে নইয়ান** হলেন Hugging Face এর একজন ডেভেলপার অ্যাডভোকেট। যিনি টুল ডেভেলপ করেন এবং সেগুলো ব্যবহার করে কন্টেন্ট তৈরি করেন যাতে মেশিন লার্নিংকে গণতান্ত্রিক করা যায়।
-
-**Lucile Saulnier** হলেন Hugging Face এর একজন মেশিন লার্নিং ইঞ্জিনিয়ার, যিনি ওপেন সোর্স টুলের ডেভেলপমেন্ট ও ব্যবহার এ সাহায্য করে থাকেন। তিনি ন্যচালার ল্যঙ্গুএজ প্রসেসিং এর পাশাপাশি collaborative training এবং বিগসায়েন্সের মতো বিষয়ের অনেক গবেষণা প্রকল্পে সক্রিয়ভাবে জড়িত।
-
-
-**Lewis Tunstall** হলেন একজন মেশিন লার্নিং ইঞ্জিনিয়ার, যিনি ওপেন-সোর্স টুল ডেভেলপ করতে এবং সেগুলিকে বৃহত্তর সম্প্রদায়ের কাছে অ্যাক্সেসযোগ্য করে তোলার দিকে মনোনিবেশ করেন৷ তিনি একটি আসন্ন একটি বইয়ের সহ-লেখক [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).
-
-
-**Leandro von Werra** হলেন Hugging Face-এর ওপেন-সোর্স টিমের একজন মেশিন লার্নিং ইঞ্জিনিয়ার এবং ট্রান্সফরমারের উপর একটি আসন্ন O'Reilly বইয়ের সহ-লেখক [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). পুরো মেশিন লার্নিং স্ট্যাক জুড়ে কাজ করে NLP প্রকল্পগুলিকে উৎপাদনে নিয়ে আসার কয়েক বছরের ইন্ডাস্ট্রি অভিজ্ঞতা রয়েছে তার।
-
-
-আপনি রোল প্রস্তুত? এই অধ্যায়ে, আপনি শিখবেন:
-* কিভাবে টেক্সট জেনারেশন এবং শ্রেণীবিভাগের মতো NLP কাজগুলি সমাধান করতে ` `pipeline()` ফাংশন ব্যবহার করবেন
-* ট্রান্সফরমার আর্কিটেকচার সম্পর্কে
-* কিভাবে এনকোডার, ডিকোডার এবং এনকোডার-ডিকোডার আর্কিটেকচারের মধ্যে পার্থক্য করা যায় এবং কেস ব্যবহার করা যায়।
+# ভূমিকা
+
+
+
+## 🤗 কোর্সে স্বাগতম!
+
+
+
+এই কোর্সটি আপনাকে [হাগিং ফেস](https://huggingface.co/) ইকোসিস্টেম থেকে — 🤗
+ [🤗 ট্রান্সফরমার](https://github.com/huggingface/transformers),[🤗 ডেটাসেট](https://github.com/huggingface/datasets), [🤗 টোকেনাইজার](https://github.com/huggingface/tokenizers),এবং [🤗 অ্যাক্সিলারেট](https://github.com/huggingface/accelerate) — সেইসাথে হাগিং ফেস হাব থেকে লাইব্রেরি ব্যবহার করে ন্যচারাল ল্যঙ্গুএজ প্রসেসিং(NLP) শেখাবে। এটি সাইট টি বিজ্ঞাপন ছাড়াই সম্পূর্ণ ফ্রি।
+
+
+## এই কোর্সটি থেকে কি আশা করা যায়?
+
+এখানে কোর্সের একটি সংক্ষিপ্ত বিবরণ রয়েছে:
+
+
+

+

+
+
+- অধ্যায় ১ থেকে ৪ 🤗 ট্রান্সফরমার লাইব্রেরির মূল ধারণাগুলির একটি ভূমিকা প্রদান করে। কোর্সের এই অংশের শেষে, আপনি ট্রান্সফরমার মডেলগুলি কীভাবে কাজ করে তার সাথে পরিচিত হবেন। এছাড়াও [হাগিং ফেস হাব](https://huggingface.co/models) থেকে একটি মডেল কীভাবে ব্যবহার করতে হয়, কীভাবে মডেল এর ডেটাসেটিকে ফাইন-টিউন করতে হয় এবং হাবে কীভাবে আপনার ফলাফল শেয়ার করতে হয় তা জানতে পারবেন!
+
+- ক্লাসিক NLP টাস্কগুলোর গভীরে যাওয়ার আগে অধ্যায় ৫ থেকে ৮, আপনাকে 🤗 ডেটাসেট এবং 🤗 টোকেনাইজারগুলির মূল বিষয়গুলি শেখাবে৷ এই অংশের শেষে, আপনি নিজেই সবচেয়ে কমন NLP সমস্যাগুলি সমাধান করতে পাড়বেন।
+
+- অধ্যায় ৯ থেকে ১২, এবং স্পীচ প্রসেসিং এবং কম্পিউটার ভিশনের কাজগুলো ট্রান্সফরমার মডেলগুলোকে কীভাবে ব্যবহার করা যেতে পারে তা খতিয়ে দেখে। অধ্যায়টি পড়তে পড়তে আপনি শিখবেন কিভাবে আপনার মডেল বিল্ড এবং ডেমো শেয়ার করতে হয়, কিভাবে প্রডাকশন এনভায়রনমেন্টের জন্য অপ্টিমাইজ করতে হয়। এই অংশের শেষ নাগাদ, (প্রায়) যেকোনও মেশিন লার্নিং সমস্যায় আপনি 🤗 ট্রান্সফরমার প্রয়োগ করতে প্রস্তুত হয়ে যাবেন!
+
+
+এই কোর্স::
+
+* পাইথন সম্পর্কে ভাল জ্ঞান প্রয়োজন
+* একটি প্রাথমিক ডিপ লার্নিং কোর্স করে নেওয়া ভালো, যেমন fast.ai-এর [fast.ai's](https://www.fast.ai/) [Practical Deep Learning for Coders](https://course.fast.ai/) বা [DeepLearning.AI](https://www.deeplearning.ai/) এর প্রোগ্রামগুলির মধ্যে একটি।
+* [PyTorch](https://pytorch.org/) বা [TensorFlow](https://www.tensorflow.org/) জানা জরুরি না, যদিও এদের যেকোনো একটির সাথে কিছু পরিচয় থাকলে সেটা আপানাকে সাহায্য করবে।
+
+আপনি এই কোর্সটি সম্পন্ন করার পরে, আমরা DeepLearning.AI-এর [Natural Language Processing Specialization](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh) কোর্সটি করার পরামর্শ দিই, যেটি প্রথাগত NLP মডেল যেমন naive Bayes এবং LSTMs সম্পর্কে জানতে আপনাকে সাহায্য করবে!
+
+## আমরা কারা??
+
+লেখক সম্পর্কে::
+
+**Matthew Carrigan ম্যাথিউ ক্যারিগান** Hugging Face এর একজন মেশিন লার্নিং ইঞ্জিনিয়ার। তিনি আয়ারল্যান্ডের ডাবলিনে থাকেন এবং পূর্বে Parse.ly-এ একজন ML ইঞ্জিনিয়ার হিসেবে এবং তার আগে ট্রিনিটি কলেজ ডাবলিন-এ একজন পোস্ট-ডক্টরাল গবেষক হিসেবে কাজ করেছেন। তিনি বিশ্বাস করেন না যে আমরা বিদ্যমান আর্কিটেকচারগুলিকে স্কেল করে AGI তে পৌছাবো, তবে তিনি দৃড়ভাবে আশা করেন যে আমারা রোবট অমরত্বের দিকে যাচ্ছি৷
+
+**Lysandre Debut লিসান্দ্রে ডেব্যু** Hugging Face এর একজন মেশিন লার্নিং ইঞ্জিনিয়ার এবং খুব প্রাথমিক পর্যায় থেকে 🤗 Transformers লাইব্রেরিতে কাজ করছেন। তার লক্ষ্য হল একটি খুব সাধারণ API-এর সাহায্যে টুল ডেভেলপ করে সবার জন্য NLP কে সুবোধ্য করে তোলা।
+
+**Sylvain Gugger সিলভাইন গুগার** হলেন হাগিং ফেসের একজন গবেষণা প্রকৌশলী এবং 🤗 ট্রান্সফরমার লাইব্রেরির মূল রক্ষণাবেক্ষণকারীদের একজন। পূর্বে তিনি fast.ai-এর একজন গবেষণা বিজ্ঞানী ছিলেন এবং জেরেমি হাওয়ার্ডের সাথে _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ বইটি লেখেন। তার গবেষণার মূল ফোকাস হল মডেল গুলিকে আরও উন্নত করে এবং অল্প রিসোর্স ব্যবহার করে ট্রেনিং ব্যবস্থা করার মাধ্যমে সবার জন্য deep learning এর কৌশলগুলি আরও সুবোধ্য করে তোলা।
+
+**Merve Noyan মার্ভে নইয়ান** হলেন Hugging Face এর একজন ডেভেলপার অ্যাডভোকেট। যিনি টুল ডেভেলপ করেন এবং সেগুলো ব্যবহার করে কন্টেন্ট তৈরি করেন যাতে মেশিন লার্নিংকে গণতান্ত্রিক করা যায়।
+
+**Lucile Saulnier** হলেন Hugging Face এর একজন মেশিন লার্নিং ইঞ্জিনিয়ার, যিনি ওপেন সোর্স টুলের ডেভেলপমেন্ট ও ব্যবহার এ সাহায্য করে থাকেন। তিনি ন্যচালার ল্যঙ্গুএজ প্রসেসিং এর পাশাপাশি collaborative training এবং বিগসায়েন্সের মতো বিষয়ের অনেক গবেষণা প্রকল্পে সক্রিয়ভাবে জড়িত।
+
+
+**Lewis Tunstall** হলেন একজন মেশিন লার্নিং ইঞ্জিনিয়ার, যিনি ওপেন-সোর্স টুল ডেভেলপ করতে এবং সেগুলিকে বৃহত্তর সম্প্রদায়ের কাছে অ্যাক্সেসযোগ্য করে তোলার দিকে মনোনিবেশ করেন৷ তিনি একটি আসন্ন একটি বইয়ের সহ-লেখক [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).
+
+
+**Leandro von Werra** হলেন Hugging Face-এর ওপেন-সোর্স টিমের একজন মেশিন লার্নিং ইঞ্জিনিয়ার এবং ট্রান্সফরমারের উপর একটি আসন্ন O'Reilly বইয়ের সহ-লেখক [O’Reilly book on Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). পুরো মেশিন লার্নিং স্ট্যাক জুড়ে কাজ করে NLP প্রকল্পগুলিকে উৎপাদনে নিয়ে আসার কয়েক বছরের ইন্ডাস্ট্রি অভিজ্ঞতা রয়েছে তার।
+
+
+আপনি রোল প্রস্তুত? এই অধ্যায়ে, আপনি শিখবেন:
+* কিভাবে টেক্সট জেনারেশন এবং শ্রেণীবিভাগের মতো NLP কাজগুলি সমাধান করতে ` `pipeline()` ফাংশন ব্যবহার করবেন
+* ট্রান্সফরমার আর্কিটেকচার সম্পর্কে
+* কিভাবে এনকোডার, ডিকোডার এবং এনকোডার-ডিকোডার আর্কিটেকচারের মধ্যে পার্থক্য করা যায় এবং কেস ব্যবহার করা যায়।
diff --git a/chapters/bn/chapter2/1.mdx b/chapters/bn/chapter2/1.mdx
index fd19b9784..5b79faee4 100644
--- a/chapters/bn/chapter2/1.mdx
+++ b/chapters/bn/chapter2/1.mdx
@@ -1,24 +1,24 @@
-# ভূমিকা
-
-
-
- [অধ্যায় ১](/course/bn/chapter1) এ আমরা দেখে এসেছি যে Transformer মডেলগুলো সাধারণত অনেক বড় হয়। লাখ-লাখ কোটি-কোটি প্যারামিটার সম্বলিত এই মডেল গুলো কে ট্রেনিং এবং ডেপ্লয় করা বেশ জটিল ও কষ্টসাধ্য একটা কাজ। তাছাড়াও প্রায় প্রতিদিনই নতুন নতুন মডেল রিলিজ হচ্ছে এবং সবগুলোরই নিজস্ব বাস্তবায়ন রয়েছে। এই সবকিছু একসাথে এপ্লাই করা খুব সহজ একটা কাজ নয়।
-
-এই 🤗 Transformers লাইব্রেরিটা বানানো হয়েছে এই সমস্যাগুলো সমাধান করার জন্য। এর আসল উদ্দেশ্য হলো এমন একটি API প্রদান করা যার মাধ্যমে যেকোনো Transformer মডেলকে লোড করা, ট্রেইন করা কিংবা সেভ করা যাবে। লাইব্রেরিটির আসল ফিচারগুলো হলঃ
-
-- **সহজে ব্যবহারযোগ্য**: ডাউনলোড করা, লোড করা এবং যেকোন state-of-the-art মডেল দিয়ে inference করা যাবে মাত্র দুই লাইনের কোড দিয়ে।
-- **ফ্লেক্সিবিলিটি**: সবগুলো Transformer মডেলই আসলে PyTorch `nn.Module` অথবা TensorFlow `tf.keras.Model` ক্লাস , আর অন্য যেকোনো মডেলের মতোই এদেরকে তাদের নিজ নিজ মেশিন লার্নিং ফ্রেমওয়ার্ক এ সহজেই পরিচালনা করা যায়।
-
-- **সরলতা**: লাইব্রেরি জুড়ে খুব কমই বিমূর্ততা তৈরি করা হয়। "All in one file" এমন একটি ধারণাঃ একটা মডেলের পুরো Forward Pass কে সম্পূর্ণরূপে একটি সিঙ্গেল ফাইলে নিয়ে আসা হয়েছে, যাতে করে কোডটি সহজেই বুঝা ও মডিফাই করা যায়।
-
-এই শেষ বৈশিষ্ট্যটি(সরলতা) 🤗 ট্রান্সফরমারকে অন্যান্য ML লাইব্রেরি থেকে বেশ আলাদা করে তোলে। এখানে মডেলগুলি কোনো মডিউল এর উপর নির্মিত নয় যেগুলো ফাইল জুড়ে শেয়ার্ড অবস্থায় থাকে; বরংচ, প্রতিটি মডেলের নিজস্ব স্তর(Layer)রয়েছে। মডেলগুলিকে আরও সহজলভ্য এবং বোধগম্য করার পাশাপাশি, 🤗 Transformers আপনাকে অন্য মডেলকে প্রভাবিত না করে সহজেই একটি মডেলে নিয়ে এক্সপেরিমেন্ট করতে দেয়৷
-
-এই অধ্যায়টি একটি পূর্নাঙ্গ উদাহরন দিয়ে শুরু হবে, যেখানে [অধ্যায় ১](/course/bn/chapter1) এ উল্লিখিত `pipeline()` ফাংশনটি প্রতিলিপি করতে আমরা একটি মডেল এবং একটি টোকেনাইজার একসাথে ব্যবহার করব। এর পরে, আমরা মডেল API নিয়ে আলোচনা করব: আমরা মডেল এবং কনফিগারেশন ক্লাসগুলির খুঁটিনাটি দেখব এবং আপনাকে দেখাব কীভাবে একটি মডেল লোড করতে হয় এবং কীভাবে এটি সংখ্যাসূচক ইনপুটগুলিকে প্রক্রিয়া করে আউটপুট প্রেডিক্ট করা যায়।
-
-তারপরে আমরা টোকেনাইজার API দেখব, যা `pipeline()` ফাংশনের অন্য একটি প্রধান উপাদান। টোকেনাইজার জিনিসটা প্রথম ও শেষ প্রসেসিং স্টেপগুলোতে মেইনলি কাজে লাগে, নিউরাল নেটওয়ার্কের জন্য টেক্সট ডাটা থেকে সংখ্যাসূচক ইনপুটে রূপান্তর এবং পরে আবার প্রয়োজন অনুযায়ী সংখ্যাসূচক ডাটা থেকে টেক্সট ডাটাতে রূপান্তর করার সময়। পরিশেষে, আমরা আপনাকে দেখাব কিভাবে ব্যাচের মাধ্যমে একাধিক বাক্যকে একটি মডেলে পাঠানো যায়। তারপরে আরেকবার হাই-লেভেলে `tokenizer()` ফাংশনটিকে একনজরে দেখার মাধ্যমে পুরো অধ্যায়ের ইতি টানব।
-
-> [!TIP]
+# ভূমিকা
+
+
+
+ [অধ্যায় ১](/course/bn/chapter1) এ আমরা দেখে এসেছি যে Transformer মডেলগুলো সাধারণত অনেক বড় হয়। লাখ-লাখ কোটি-কোটি প্যারামিটার সম্বলিত এই মডেল গুলো কে ট্রেনিং এবং ডেপ্লয় করা বেশ জটিল ও কষ্টসাধ্য একটা কাজ। তাছাড়াও প্রায় প্রতিদিনই নতুন নতুন মডেল রিলিজ হচ্ছে এবং সবগুলোরই নিজস্ব বাস্তবায়ন রয়েছে। এই সবকিছু একসাথে এপ্লাই করা খুব সহজ একটা কাজ নয়।
+
+এই 🤗 Transformers লাইব্রেরিটা বানানো হয়েছে এই সমস্যাগুলো সমাধান করার জন্য। এর আসল উদ্দেশ্য হলো এমন একটি API প্রদান করা যার মাধ্যমে যেকোনো Transformer মডেলকে লোড করা, ট্রেইন করা কিংবা সেভ করা যাবে। লাইব্রেরিটির আসল ফিচারগুলো হলঃ
+
+- **সহজে ব্যবহারযোগ্য**: ডাউনলোড করা, লোড করা এবং যেকোন state-of-the-art মডেল দিয়ে inference করা যাবে মাত্র দুই লাইনের কোড দিয়ে।
+- **ফ্লেক্সিবিলিটি**: সবগুলো Transformer মডেলই আসলে PyTorch `nn.Module` অথবা TensorFlow `tf.keras.Model` ক্লাস , আর অন্য যেকোনো মডেলের মতোই এদেরকে তাদের নিজ নিজ মেশিন লার্নিং ফ্রেমওয়ার্ক এ সহজেই পরিচালনা করা যায়।
+
+- **সরলতা**: লাইব্রেরি জুড়ে খুব কমই বিমূর্ততা তৈরি করা হয়। "All in one file" এমন একটি ধারণাঃ একটা মডেলের পুরো Forward Pass কে সম্পূর্ণরূপে একটি সিঙ্গেল ফাইলে নিয়ে আসা হয়েছে, যাতে করে কোডটি সহজেই বুঝা ও মডিফাই করা যায়।
+
+এই শেষ বৈশিষ্ট্যটি(সরলতা) 🤗 ট্রান্সফরমারকে অন্যান্য ML লাইব্রেরি থেকে বেশ আলাদা করে তোলে। এখানে মডেলগুলি কোনো মডিউল এর উপর নির্মিত নয় যেগুলো ফাইল জুড়ে শেয়ার্ড অবস্থায় থাকে; বরংচ, প্রতিটি মডেলের নিজস্ব স্তর(Layer)রয়েছে। মডেলগুলিকে আরও সহজলভ্য এবং বোধগম্য করার পাশাপাশি, 🤗 Transformers আপনাকে অন্য মডেলকে প্রভাবিত না করে সহজেই একটি মডেলে নিয়ে এক্সপেরিমেন্ট করতে দেয়৷
+
+এই অধ্যায়টি একটি পূর্নাঙ্গ উদাহরন দিয়ে শুরু হবে, যেখানে [অধ্যায় ১](/course/bn/chapter1) এ উল্লিখিত `pipeline()` ফাংশনটি প্রতিলিপি করতে আমরা একটি মডেল এবং একটি টোকেনাইজার একসাথে ব্যবহার করব। এর পরে, আমরা মডেল API নিয়ে আলোচনা করব: আমরা মডেল এবং কনফিগারেশন ক্লাসগুলির খুঁটিনাটি দেখব এবং আপনাকে দেখাব কীভাবে একটি মডেল লোড করতে হয় এবং কীভাবে এটি সংখ্যাসূচক ইনপুটগুলিকে প্রক্রিয়া করে আউটপুট প্রেডিক্ট করা যায়।
+
+তারপরে আমরা টোকেনাইজার API দেখব, যা `pipeline()` ফাংশনের অন্য একটি প্রধান উপাদান। টোকেনাইজার জিনিসটা প্রথম ও শেষ প্রসেসিং স্টেপগুলোতে মেইনলি কাজে লাগে, নিউরাল নেটওয়ার্কের জন্য টেক্সট ডাটা থেকে সংখ্যাসূচক ইনপুটে রূপান্তর এবং পরে আবার প্রয়োজন অনুযায়ী সংখ্যাসূচক ডাটা থেকে টেক্সট ডাটাতে রূপান্তর করার সময়। পরিশেষে, আমরা আপনাকে দেখাব কিভাবে ব্যাচের মাধ্যমে একাধিক বাক্যকে একটি মডেলে পাঠানো যায়। তারপরে আরেকবার হাই-লেভেলে `tokenizer()` ফাংশনটিকে একনজরে দেখার মাধ্যমে পুরো অধ্যায়ের ইতি টানব।
+
+> [!TIP]
> ⚠️ Model Hub এবং 🤗 Transformers এর সাথে উপলব্ধ সমস্ত বৈশিষ্ট্যগুলি থেকে উপকৃত হওয়ার জন্য, আমরা সাজেস্ট করি এখানে একটি একাউন্ট তৈরি করার জন্যে।.
\ No newline at end of file
diff --git a/chapters/de/TRANSLATING.txt b/chapters/de/TRANSLATING.txt
index 053f5930d..9649c90c0 100644
--- a/chapters/de/TRANSLATING.txt
+++ b/chapters/de/TRANSLATING.txt
@@ -1,46 +1,46 @@
-1. We use the informal "you" (i.e. "Du" instead of "Sie") to keep the tone jovial.
- However, don't use slang or local language, so use the correct form of "als",
- and "wie" rather than the locally accepted form.
-
-2. Don't translate industry-accepted acronyms. e.g. TPU or GPU.
-
-3. The German language accepts English words especially in modern contexts more than
- many other languages (i.e. Anglicisms). Check for the correct usage of terms in
- computer science and commonly used terms in other publications.
-
-4. Beware of "false friends" in German and English translations. Translators are trained
- for years to specifically avoid false English friends and avoid anglicised translations.
- e.g. "Daten" is "data", but "dates" is "Termine". For more examples refer to:
- https://lal.de/blog/false-friends-falsche-freunde/
-
-5. Keep voice active and consistent. Don't overdo it but try to avoid a passive voice.
-
-6. Refer and contribute to the glossary frequently to stay on top of the latest
- choices we make. This minimizes the amount of editing that is required.
-
-7. Keep POV consistent.
-
-8. Smaller sentences are better sentences. Apply with nuance.
-
-9. If translating a technical word, keep the choice of German translation consistent.
- This does not apply for non-technical choices, as in those cases variety actually
- helps keep the text engaging.
-
-10. This is merely a translation. Don't add any technical/contextual information
- not present in the original text. Also don't leave stuff out. The creative
- choices in composing this information were the original authors' to make.
- Our creative choices are in doing a quality translation.
-
-11. Be exact when choosing equivalents for technical words. Package is package.
- Library is library. Don't mix and match.
-
-12. Library names are kept in the original forms, e.g. "🤗 Datasets", however,
- the word dataset in a sentence gets a translation to "Datensatz".
-
-13. As a style choice prefer the imperative over constructions with auxiliary words
- to avoid unnecessary verbosity and addressing of the reader, which seems
- unnatural in German. e.g. "Siehe Kapitel X" - "See chapter X" instead of
- "Dies kannst du in Kapitel X sehen" - "You can see this in chapter X".
-
-14. Be careful with sentences using "wir", which can seem unnatural at best and
+1. We use the informal "you" (i.e. "Du" instead of "Sie") to keep the tone jovial.
+ However, don't use slang or local language, so use the correct form of "als",
+ and "wie" rather than the locally accepted form.
+
+2. Don't translate industry-accepted acronyms. e.g. TPU or GPU.
+
+3. The German language accepts English words especially in modern contexts more than
+ many other languages (i.e. Anglicisms). Check for the correct usage of terms in
+ computer science and commonly used terms in other publications.
+
+4. Beware of "false friends" in German and English translations. Translators are trained
+ for years to specifically avoid false English friends and avoid anglicised translations.
+ e.g. "Daten" is "data", but "dates" is "Termine". For more examples refer to:
+ https://lal.de/blog/false-friends-falsche-freunde/
+
+5. Keep voice active and consistent. Don't overdo it but try to avoid a passive voice.
+
+6. Refer and contribute to the glossary frequently to stay on top of the latest
+ choices we make. This minimizes the amount of editing that is required.
+
+7. Keep POV consistent.
+
+8. Smaller sentences are better sentences. Apply with nuance.
+
+9. If translating a technical word, keep the choice of German translation consistent.
+ This does not apply for non-technical choices, as in those cases variety actually
+ helps keep the text engaging.
+
+10. This is merely a translation. Don't add any technical/contextual information
+ not present in the original text. Also don't leave stuff out. The creative
+ choices in composing this information were the original authors' to make.
+ Our creative choices are in doing a quality translation.
+
+11. Be exact when choosing equivalents for technical words. Package is package.
+ Library is library. Don't mix and match.
+
+12. Library names are kept in the original forms, e.g. "🤗 Datasets", however,
+ the word dataset in a sentence gets a translation to "Datensatz".
+
+13. As a style choice prefer the imperative over constructions with auxiliary words
+ to avoid unnecessary verbosity and addressing of the reader, which seems
+ unnatural in German. e.g. "Siehe Kapitel X" - "See chapter X" instead of
+ "Dies kannst du in Kapitel X sehen" - "You can see this in chapter X".
+
+14. Be careful with sentences using "wir", which can seem unnatural at best and
condescending at worst in German.
\ No newline at end of file
diff --git a/chapters/de/_toctree.yml b/chapters/de/_toctree.yml
index cf4af10c2..d16e3320e 100644
--- a/chapters/de/_toctree.yml
+++ b/chapters/de/_toctree.yml
@@ -1,66 +1,66 @@
-- title: 0. Einrichtung
- sections:
- - local: chapter0/1
- title: Einführung
-
-- title: 1. Transformer-Modelle
- sections:
- - local: chapter1/1
- title: Einführung
- - local: chapter1/2
- title: Natural Language Processing
- - local: chapter1/3
- title: Transformer-Modelle - wozu sind sie imstande?
- - local: chapter1/4
- title: Wie funktionieren Transformer-Modelle?
- - local: chapter1/5
- title: Encoder-Modelle
- - local: chapter1/6
- title: Decoder-Modelle
- - local: chapter1/7
- title: Sequence-to-Sequence-Modelle
- - local: chapter1/8
- title: Bias und Einschränkungen
- - local: chapter1/9
- title: Zusammenfassung
- - local: chapter1/10
- title: Quiz am Ende des Kapitels
- quiz: 1
-
-- title: 3. Fine-tuning von vortrainierten Modellen
- sections:
- - local: chapter3/1
- title: Einführung
- - local: chapter3/2
- title: Datenbearbeitung
- - local: chapter3/3
- title: Fine-tuning von Modellen mit der Trainer API oder Keras
- local_fw: { pt: chapter3/3, tf: chapter3/3_tf }
- - local: chapter3/4
- title: Komplettes Training
- - local: chapter3/5
- title: Fine-tuning, Check!
- - local: chapter3/6
- title: Quiz am Ende des Kapitels
- quiz: 3
-
-- title: 4. Teilen von Modellen und Tokenizers
- sections:
- - local: chapter4/1
- title: Der Hugging Face Hub
- - local: chapter4/2
- title: Verwendung vortrainierter Modelle
- - local: chapter4/3
- title: Vortrainierte Modelle teilen
- - local: chapter4/4
- title: Erstellung einer Modellkarte ("model card")
- - local: chapter4/5
- title: Teil 1 abgeschlossen!
- - local: chapter4/6
- title: Quiz am Ende des Kapitels
- quiz: 4
-
-- title: Wörterverzeichnis
- sections:
- - local: glossary/1
- title: Wörterverzeichnis
+- title: 0. Einrichtung
+ sections:
+ - local: chapter0/1
+ title: Einführung
+
+- title: 1. Transformer-Modelle
+ sections:
+ - local: chapter1/1
+ title: Einführung
+ - local: chapter1/2
+ title: Natural Language Processing
+ - local: chapter1/3
+ title: Transformer-Modelle - wozu sind sie imstande?
+ - local: chapter1/4
+ title: Wie funktionieren Transformer-Modelle?
+ - local: chapter1/5
+ title: Encoder-Modelle
+ - local: chapter1/6
+ title: Decoder-Modelle
+ - local: chapter1/7
+ title: Sequence-to-Sequence-Modelle
+ - local: chapter1/8
+ title: Bias und Einschränkungen
+ - local: chapter1/9
+ title: Zusammenfassung
+ - local: chapter1/10
+ title: Quiz am Ende des Kapitels
+ quiz: 1
+
+- title: 3. Fine-tuning von vortrainierten Modellen
+ sections:
+ - local: chapter3/1
+ title: Einführung
+ - local: chapter3/2
+ title: Datenbearbeitung
+ - local: chapter3/3
+ title: Fine-tuning von Modellen mit der Trainer API oder Keras
+ local_fw: { pt: chapter3/3, tf: chapter3/3_tf }
+ - local: chapter3/4
+ title: Komplettes Training
+ - local: chapter3/5
+ title: Fine-tuning, Check!
+ - local: chapter3/6
+ title: Quiz am Ende des Kapitels
+ quiz: 3
+
+- title: 4. Teilen von Modellen und Tokenizers
+ sections:
+ - local: chapter4/1
+ title: Der Hugging Face Hub
+ - local: chapter4/2
+ title: Verwendung vortrainierter Modelle
+ - local: chapter4/3
+ title: Vortrainierte Modelle teilen
+ - local: chapter4/4
+ title: Erstellung einer Modellkarte ("model card")
+ - local: chapter4/5
+ title: Teil 1 abgeschlossen!
+ - local: chapter4/6
+ title: Quiz am Ende des Kapitels
+ quiz: 4
+
+- title: Wörterverzeichnis
+ sections:
+ - local: glossary/1
+ title: Wörterverzeichnis
diff --git a/chapters/de/chapter0/1.mdx b/chapters/de/chapter0/1.mdx
index 9b9fd8805..e9bddf15a 100644
--- a/chapters/de/chapter0/1.mdx
+++ b/chapters/de/chapter0/1.mdx
@@ -1,110 +1,110 @@
-# Einführung
-
-Willkommen zum Hugging-Face-Kurs! Die vorliegende Einführung wird dir dabei helfen, deine Arbeitsumgebung einzurichten. Wenn du den Kurs gerade erst beginnst, empfehlen wir dir, zuerst einen Überblick über [Kapitel 1](/course/chapter1) zu gewinnen und dann wieder hierher zurückzukommen und deine Umgebung einzurichten, damit du den Code selbst ausprobieren kannst.
-
-Alle Bibliotheken, die wir in diesem Kurs verwenden werden, sind als Python-Pakete verfügbar. Daher zeigen wir dir hier, wie du eine Python-Umgebung einrichtest und die benötigten Bibliotheken installierst.
-
-Wir stellen zwei verschiedene Möglichkeiten vor, wie du deine Arbeitsumgebung einrichten kannst: entweder über ein Colab-Notebook oder über eine virtuelle Python-Umgebung. Du kannst dich für die Variante entscheiden, die dir am meisten zusagt. Anfängern empfehlen wir dringend, mit einem Colab-Notebook zu beginnen.
-
-Wir werden nicht auf das Windows-System eingehen. Wenn du auf einem Windows-System arbeitest, empfehlen wir dir, ein Colab-Notebook zu verwenden. Wenn du eine Linux-Distribution oder macOS verwendest, kannst du beide der hier beschriebenen Ansätze nutzen.
-
-Für den Großteil des Kurses ist es erforderlich, dass du ein Konto bei Hugging Face hast. Wir raten dir, am besten gleich eines zu erstellen: [Konto erstellen](https://huggingface.co/join).
-
-## Verwendung eines Google-Colab-Notebooks
-
-Ein Colab-Notebook zu verwenden, ist die einfachste Lösung: Starte einfach ein Notebook in deinem Browser und fange direkt an zu programmieren!
-
-Falls du mit Colab noch nicht vertraut sein solltest, empfehlen wir dir, zunächst mit der [bereitstehenden Einführung](https://colab.research.google.com/notebooks/intro.ipynb) zu beginnen. Colab ermöglicht die Verwendung von beschleunigter Hardware, wie GPUs oder TPUs, und ist für kleinere Workloads kostenlos.
-
-Sobald du dich in Colab ausreichend zurechtfindest, kannst du ein neues Notebook anlegen und mit der Einrichtung beginnen:
-
-
-

-
-
-Im nächsten Schritt installieren wir die Bibliotheken, die wir in diesem Kurs verwenden werden. Für die Installation werden wir die Paketverwaltung für Python, `pip`, verwenden. Um Systembefehle in Notebooks ausführen zu können, musst du ihnen das Zeichen `!` voranstellen. Dementsprechend kannst du die 🤗 Transformers-Bibliothek wie folgt installieren:
-
-```
-!pip install transformers
-```
-
-Wenn du gleich sicherstellen möchtest, dass das Paket korrekt installiert wurde, kannst du es einfach in deiner Python-Laufzeitumgebung importieren:
-
-```
-import transformers
-```
-
-
-

-
-
-Damit wird eine sehr abgespeckte Version der 🤗 Transformers-Bibliothek installiert. Insbesondere werden keine spezifischen Frameworks für maschinelles Lernen (wie PyTorch oder TensorFlow) installiert. Da wir viele verschiedene Funktionen der Bibliothek verwenden werden, empfehlen wir die Installation der Entwicklungs- bzw. Development-Version, die alle erforderlichen Abhängigkeiten für so ziemlich jeden erdenklichen Anwendungsfall enthält:
-
-```
-!pip install transformers[sentencepiece]
-```
-
-Dies dauert zwar etwas länger, aber anschließend bist du bereit für den Rest des Kurses!
-
-## Verwendung einer virtuellen Python-Umgebung
-
-Wenn du lieber eine virtuelle Python-Umgebung verwenden möchtest, musst du zunächst Python auf deinem System installieren. Für den Anfang empfehlen wir [diese Anleitung](https://realpython.com/installing-python/).
-
-Sobald du Python installiert hast, solltest du in der Lage sein, Python-Befehle in deinem Terminal auszuführen. Zunächst kannst du den folgenden Befehl ausführen, um sicherzustellen, dass Python korrekt installiert ist, bevor du mit den nächsten Schritten fortfährst: `python --version`. Dies sollte die Python-Version ausgeben, die jetzt auf deinem System installiert ist.
-
-Wenn du einen Python-Befehl in deinem Terminal ausführst, z. B. `python --version`, solltest du das Programm, das deinen Befehl ausführt, als Hauptinstallation von Python auf deinem System betrachten. Wir empfehlen, diese Hauptinstallation frei von Paketen zu halten und für jede Anwendung, an der du arbeitest, eine eigene Umgebung zu erstellen. Auf diese Weise kann jede Anwendung ihre eigenen Abhängigkeiten und Pakete haben, und du musst dir keine Gedanken über mögliche Kompatibilitätsprobleme mit anderen Anwendungen machen.
-
-In Python wird dies mit [*virtuellen Umgebungen*](https://docs.python.org/3/tutorial/venv.html) bewerkstelligt. Das sind in sich geschlossene Verzeichnisbäume, die jeweils eine Python-Installation mit einer bestimmten Python-Version sowie alle Pakete enthalten, die die Anwendung benötigt. Eine solche virtuelle Umgebung kann mit verschiedenen Werkzeugen erstellt werden. Wir werden dafür auf das offizielle Python-Paket [`venv`](https://docs.python.org/3/library/venv.html#module-venv) zurückgreifen.
-
-Erstelle zunächst das Verzeichnis, in dem du deine Anwendung unterbringen möchtest - zum Beispiel könntest du ein neues Verzeichnis namens *transformers-course* als Unterverzeichnis deines Hauptverzeichnisses (*home*) anlegen:
-
-```
-mkdir ~/transformers-course
-cd ~/transformers-course
-```
-
-Erstelle nun in diesem Verzeichnis mithilfe des Python-Moduls `venv` eine virtuelle Umgebung:
-
-```
-python -m venv .env
-```
-
-Du solltest jetzt ein Verzeichnis namens *.env* in deinem ansonsten leeren Ordner haben:
-
-```
-ls -a
-```
-
-```out
-. .. .env
-```
-
-Mit den Skripten "activate" und "deactivate" kannst du in deine virtuelle Umgebung hinein- und herauswechseln:
-
-```
-# Aktivieren der virtuellen Umgebung
-source .env/bin/activate
-
-# Deaktivieren der virtuellen Umgebung
-deactivate
-```
-
-Du kannst dich vergewissern, dass die Umgebung aktiviert ist, indem du den Befehl `which python` ausführst: Wenn er auf die virtuelle Umgebung verweist, dann hast du sie erfolgreich aktiviert!
-
-```
-which python
-```
-
-```out
-/home//transformers-course/.env/bin/python
-```
-
-### Installieren von Abhängigkeiten
-
-Wie im vorherigen Abschnitt zur Verwendung von Google-Colab-Instanzen musst du nun noch die Pakete installieren, die du zum Fortfahren benötigst. Auch hier empfehlen wir, die Entwicklungsversion von 🤗 Transformers mithilfe des Paketverwaltungsprogramm `pip` zu installieren:
-
-```
-pip install "transformers[sentencepiece]"
-```
-
-So, nun bist du startklar und kannst loslegen!
+# Einführung
+
+Willkommen zum Hugging-Face-Kurs! Die vorliegende Einführung wird dir dabei helfen, deine Arbeitsumgebung einzurichten. Wenn du den Kurs gerade erst beginnst, empfehlen wir dir, zuerst einen Überblick über [Kapitel 1](/course/chapter1) zu gewinnen und dann wieder hierher zurückzukommen und deine Umgebung einzurichten, damit du den Code selbst ausprobieren kannst.
+
+Alle Bibliotheken, die wir in diesem Kurs verwenden werden, sind als Python-Pakete verfügbar. Daher zeigen wir dir hier, wie du eine Python-Umgebung einrichtest und die benötigten Bibliotheken installierst.
+
+Wir stellen zwei verschiedene Möglichkeiten vor, wie du deine Arbeitsumgebung einrichten kannst: entweder über ein Colab-Notebook oder über eine virtuelle Python-Umgebung. Du kannst dich für die Variante entscheiden, die dir am meisten zusagt. Anfängern empfehlen wir dringend, mit einem Colab-Notebook zu beginnen.
+
+Wir werden nicht auf das Windows-System eingehen. Wenn du auf einem Windows-System arbeitest, empfehlen wir dir, ein Colab-Notebook zu verwenden. Wenn du eine Linux-Distribution oder macOS verwendest, kannst du beide der hier beschriebenen Ansätze nutzen.
+
+Für den Großteil des Kurses ist es erforderlich, dass du ein Konto bei Hugging Face hast. Wir raten dir, am besten gleich eines zu erstellen: [Konto erstellen](https://huggingface.co/join).
+
+## Verwendung eines Google-Colab-Notebooks
+
+Ein Colab-Notebook zu verwenden, ist die einfachste Lösung: Starte einfach ein Notebook in deinem Browser und fange direkt an zu programmieren!
+
+Falls du mit Colab noch nicht vertraut sein solltest, empfehlen wir dir, zunächst mit der [bereitstehenden Einführung](https://colab.research.google.com/notebooks/intro.ipynb) zu beginnen. Colab ermöglicht die Verwendung von beschleunigter Hardware, wie GPUs oder TPUs, und ist für kleinere Workloads kostenlos.
+
+Sobald du dich in Colab ausreichend zurechtfindest, kannst du ein neues Notebook anlegen und mit der Einrichtung beginnen:
+
+
+

+
+
+Im nächsten Schritt installieren wir die Bibliotheken, die wir in diesem Kurs verwenden werden. Für die Installation werden wir die Paketverwaltung für Python, `pip`, verwenden. Um Systembefehle in Notebooks ausführen zu können, musst du ihnen das Zeichen `!` voranstellen. Dementsprechend kannst du die 🤗 Transformers-Bibliothek wie folgt installieren:
+
+```
+!pip install transformers
+```
+
+Wenn du gleich sicherstellen möchtest, dass das Paket korrekt installiert wurde, kannst du es einfach in deiner Python-Laufzeitumgebung importieren:
+
+```
+import transformers
+```
+
+
+

+
+
+Damit wird eine sehr abgespeckte Version der 🤗 Transformers-Bibliothek installiert. Insbesondere werden keine spezifischen Frameworks für maschinelles Lernen (wie PyTorch oder TensorFlow) installiert. Da wir viele verschiedene Funktionen der Bibliothek verwenden werden, empfehlen wir die Installation der Entwicklungs- bzw. Development-Version, die alle erforderlichen Abhängigkeiten für so ziemlich jeden erdenklichen Anwendungsfall enthält:
+
+```
+!pip install transformers[sentencepiece]
+```
+
+Dies dauert zwar etwas länger, aber anschließend bist du bereit für den Rest des Kurses!
+
+## Verwendung einer virtuellen Python-Umgebung
+
+Wenn du lieber eine virtuelle Python-Umgebung verwenden möchtest, musst du zunächst Python auf deinem System installieren. Für den Anfang empfehlen wir [diese Anleitung](https://realpython.com/installing-python/).
+
+Sobald du Python installiert hast, solltest du in der Lage sein, Python-Befehle in deinem Terminal auszuführen. Zunächst kannst du den folgenden Befehl ausführen, um sicherzustellen, dass Python korrekt installiert ist, bevor du mit den nächsten Schritten fortfährst: `python --version`. Dies sollte die Python-Version ausgeben, die jetzt auf deinem System installiert ist.
+
+Wenn du einen Python-Befehl in deinem Terminal ausführst, z. B. `python --version`, solltest du das Programm, das deinen Befehl ausführt, als Hauptinstallation von Python auf deinem System betrachten. Wir empfehlen, diese Hauptinstallation frei von Paketen zu halten und für jede Anwendung, an der du arbeitest, eine eigene Umgebung zu erstellen. Auf diese Weise kann jede Anwendung ihre eigenen Abhängigkeiten und Pakete haben, und du musst dir keine Gedanken über mögliche Kompatibilitätsprobleme mit anderen Anwendungen machen.
+
+In Python wird dies mit [*virtuellen Umgebungen*](https://docs.python.org/3/tutorial/venv.html) bewerkstelligt. Das sind in sich geschlossene Verzeichnisbäume, die jeweils eine Python-Installation mit einer bestimmten Python-Version sowie alle Pakete enthalten, die die Anwendung benötigt. Eine solche virtuelle Umgebung kann mit verschiedenen Werkzeugen erstellt werden. Wir werden dafür auf das offizielle Python-Paket [`venv`](https://docs.python.org/3/library/venv.html#module-venv) zurückgreifen.
+
+Erstelle zunächst das Verzeichnis, in dem du deine Anwendung unterbringen möchtest - zum Beispiel könntest du ein neues Verzeichnis namens *transformers-course* als Unterverzeichnis deines Hauptverzeichnisses (*home*) anlegen:
+
+```
+mkdir ~/transformers-course
+cd ~/transformers-course
+```
+
+Erstelle nun in diesem Verzeichnis mithilfe des Python-Moduls `venv` eine virtuelle Umgebung:
+
+```
+python -m venv .env
+```
+
+Du solltest jetzt ein Verzeichnis namens *.env* in deinem ansonsten leeren Ordner haben:
+
+```
+ls -a
+```
+
+```out
+. .. .env
+```
+
+Mit den Skripten "activate" und "deactivate" kannst du in deine virtuelle Umgebung hinein- und herauswechseln:
+
+```
+# Aktivieren der virtuellen Umgebung
+source .env/bin/activate
+
+# Deaktivieren der virtuellen Umgebung
+deactivate
+```
+
+Du kannst dich vergewissern, dass die Umgebung aktiviert ist, indem du den Befehl `which python` ausführst: Wenn er auf die virtuelle Umgebung verweist, dann hast du sie erfolgreich aktiviert!
+
+```
+which python
+```
+
+```out
+/home//transformers-course/.env/bin/python
+```
+
+### Installieren von Abhängigkeiten
+
+Wie im vorherigen Abschnitt zur Verwendung von Google-Colab-Instanzen musst du nun noch die Pakete installieren, die du zum Fortfahren benötigst. Auch hier empfehlen wir, die Entwicklungsversion von 🤗 Transformers mithilfe des Paketverwaltungsprogramm `pip` zu installieren:
+
+```
+pip install "transformers[sentencepiece]"
+```
+
+So, nun bist du startklar und kannst loslegen!
diff --git a/chapters/de/chapter1/1.mdx b/chapters/de/chapter1/1.mdx
index 14e19601c..5213759c8 100644
--- a/chapters/de/chapter1/1.mdx
+++ b/chapters/de/chapter1/1.mdx
@@ -1,102 +1,102 @@
-# Einführung
-
-
-
-## Willkommen zum 🤗 Kurs!
-
-
-
-In diesem Kurs lernst du verschiedene Teilbereiche der maschinellen Verarbeitung natürlicher Sprache (engl. Natural Language Processing, NLP) - im Deutschen auch als Maschinelle Sprachverarbeitung oder Computerlinguistik (CL) bezeichnet - unter Verwendung der Bibliotheken des Ökosystems von [Hugging Face](https://huggingface.co/) kennen: die [🤗 Transformers-](https://github.com/huggingface/transformers), die [🤗 Datasets-](https://github.com/huggingface/datasets), die [🤗 Tokenizers-](https://github.com/huggingface/tokenizers) sowie die [🤗 Accelerate-Bibliotheken](https://github.com/huggingface/accelerate) als auch der [Hugging Face Hub](https://huggingface.co/models). Der Kurs ist komplett kostenlos und frei von Werbung.
-
-
-## Was erwartet dich?
-
-Hier ein kurzer Überblick über den Kurs:
-
-
-

-

-
-
-- Die Kapitel 1 bis 4 geben eine Einführung in die wichtigsten Konzepte der 🤗 Transformers-Bibliothek. Am Ende dieses Teils des Kurses wirst du mit der Funktionsweise von Transformer-Modellen vertraut sein und wissen, wie du ein Modell aus dem [Hugging Face Hub](https://huggingface.co/models) verwendest, es auf einem Datensatz feintunst und deine Ergebnisse mit anderen auf dem Hub teilst!
-- In den Kapiteln 5 bis 8 lernst du die Grundlagen der 🤗 Datasets- und 🤗 Tokenizers-Bibliotheken kennen, bevor du in die typischen Problemstellungen des NLP eintauchst. Am Ende dieses Teils wirst du in der Lage sein, die gängisten Problemstellungen im NLP selbstständig zu lösen.
-- Die Kapitel 9 bis 12 gehen über den Bereich des NLP hinaus und zeigen, wie Transformer-Modelle für Aufgaben bei der Verarbeitung gesprochener Sprache (engl. Speech Processing) und im Bereich Computer Vision (im Deutschen ungefähr mit computerbasiertem Sehen zu übersetzen) eingesetzt werden können. Nebenbei lernst du, wie du eigene Versionen deiner Modelle zu Demonstrationszwecken erstellen und sie mit anderen teilen kannst, und wie du sie für Produktionsumgebungen optimierst. Am Ende dieses Teils wirst du in der Lage sein, die 🤗 Transformers-Bibliothek auf (fast) jede Problemstellung, die dir im Bereich des Maschinellen Lernens begegnen, anzuwenden!
-
-Dieser Kurs:
-
-* Erfordert gute Kenntnisse in Python
-* Sollte am besten nach einem Einführungskurs in Deep Learning gemacht werden, wie z. B. [fast.ai's Kurs](https://www.fast.ai/) [Practical Deep Learning for Coders](https://course.fast.ai/) oder eines der von [DeepLearning.AI](https://www.deeplearning.ai/) entwickelten Kursprogramme
-* Setzt keine Vorkenntnisse in [PyTorch](https://pytorch.org/) oder [TensorFlow](https://www.tensorflow.org/) voraus, obwohl es hilfreich ist, wenn du bereits mit ihnen vertraut sein solltest.
-
-Nachdem du diesen Kurs abgeschlossen hast, empfehlen wir dir den [Spezialisierungskurs Natural Language Processing von DeepLearning.AI](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh), der eine breite Palette traditioneller NLP-Modelle wie Naive Bayes und LSTMs abdeckt, bei denen es sich lohnt, sich mit ihnen vertraut zu machen!
-
-## Wer sind wir?
-
-Über die Autorinnen und Autoren:
-
-**Matthew Carrigan** ist Machine Learning Engineer bei Hugging Face. Er lebt in der irischen Hauptstadt Dublin und hat zuvor als Machine Learning Engineer bei Parse.ly und als Post-Doktorand am Trinity College Dublin gearbeitet. Er glaubt nicht, dass wir eine künstliche allgemeine Intelligenz (engl. Artificial General Intelligence, AGI) durch eine zunehmende Skalierung bestehender Architekturen erreichen werden, hat aber dennoch die Hoffnung, dass Roboter auf dem Weg zur Unsterblichkeit sind.
-
-**Lysandre Debut** ist Machine Learning Engineer bei Hugging Face und arbeitet bereits seit Entstehung an der 🤗 Transformers-Bibliothek mit. Sein Ziel ist es, NLP für alle zugänglich zu machen, indem er Tools entwickelt, die eine sehr einfache API bieten.
-
-**Sylvain Gugger** ist Research Engineer bei Hugging Face und einer der Hauptverantwortlichen für die Pflege der 🤗 Transformers-Bibliothek. Zuvor war er Research Scientist bei fast.ai und hat zusammen mit Jeremy Howard das Buch _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ verfasst. Seine Forschung ist darauf ausgerichtet, Deep Learning zugänglicher zu machen. Hierfür entwickelt und verbessert er Techniken, mit denen Modelle auch bei begrenzter Ressourcenausstattung auf schnelle Weise trainiert werden können.
-
-**Merve Noyan** ist Developer Advocate bei Hugging Face und arbeitet daran, Tools zu entwickeln und Inhalte zu erstellen, die Maschinelles Lernen für jeden zugänglich machen.
-
-**Lucile Saulnier** ist Machine Learning Engineer bei Hugging Face und entwickelt und unterstützt die Nutzung von Open-Source-Tools. Außerdem ist sie aktiv an vielen Forschungsprojekten im Bereich des NLP beteiligt, z. B. an kollaborativem Training und BigScience.
-
-**Lewis Tunstall** ist Machine Learning Engineer bei Hugging Face, und konzentriert sich darauf, Open-Source-Tools zu entwickeln und sie der breiten Community zugänglich zu machen. Zudem ist er Mitverfasser des O'Reilly-Buches [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).
-
-**Leandro von Werra** ist Machine Learning Engineer im Open-Source-Team von Hugging Face und ebenfalls einer der Autoren des O'Reilly-Buches [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). Er hat mehrere Jahre praktische Erfahrung darin gesammelt, NLP-Projekte in die Produktion zu bringen, und dabei den gesamten ML-Stack beackert.
-
-## Häufig gestellte Fragen (FAQ)
-
-Hier findest du einige Antworten auf häufig gestellte Fragen:
-
-- **Erhalte ich für die Teilnahme an diesem Kurs ein Zertifikat?
-Derzeit gibt es für diesen Kurs noch kein Zertifikat. Wir arbeiten jedoch an einem Programm zur Erlangung eines Zertifikats für das Hugging-Face-Ökosystem - bleib' auf dem Laufenden!
-
-- **Wie viel Zeit sollte ich für diesen Kurs einplanen?
-Jedes Kapitel dieses Kurses ist so konzipiert, dass es innerhalb einer Woche abgeschlossen werden kann, wenn du circa 6 bis 8 Stunden Arbeit einplanst. Du kannst dir jedoch so viel Zeit nehmen wie nötig.
-
-- **Wo kann ich Fragen stellen, wenn ich welche habe?**
-Wenn du eine Frage zu einem Kursabschnitt hast, klicke einfach auf das sich oben auf der Seite befindende Banner "*Ask a question*" und du wirst automatisch zum entsprechenden Bereich des [Hugging-Face-Forums](https://discuss.huggingface.co/) weitergeleitet:
-
-
-
-Wenn du nach dem Kurs noch weiter üben möchtest, steht dir in den Foren eine Liste mit [Projektideen](https://discuss.huggingface.co/c/course/course-event/25) zur Verfügung.
-
-- **Wo finde ich den Code für den Kurs?**
-In jedem Abschnitt kannst du auf das oben auf der Seite befindliche Banner klicken, um den Code entweder in Google Colab oder in Amazon SageMaker Studio Lab auszuführen:
-
-
-
-Die Jupyter-Notebooks, die den gesamten Code des Kurses enthalten, befinden sich im [`huggingface/notebooks`-Repo](https://github.com/huggingface/notebooks). Wenn du sie lokal aufsetzen möchtest, schau dir die Anweisungen im [`course`-Repository](https://github.com/huggingface/course#-jupyter-notebooks) auf GitHub an.
-
-
-- **Wie kann ich etwas zum Kurs beitragen?**
-Es gibt mehrere Möglichkeiten, zum Kurs beizutragen! Wenn du einen Tippfehler oder einen Fehler entdeckst, eröffne bitte ein Issue in dem [`course`-Repository](https://github.com/huggingface/course). Wenn du uns dabei unterstützen möchtest, den Kurs in deine Muttersprache zu übersetzen, sieh dir bitte die [Anleitung](https://github.com/huggingface/course#translating-the-course-into-your-language) an.
-
-- **Welche Entscheidungen wurden bei den einzelnen Übersetzungen getroffen?**
-Für jede Übersetzung gibt es ein Glossar und die Datei `TRANSLATING.txt`, in der die gewählten Fachtermini usw. festgehalten sind. Ein Beispiel für die deutsche Fassung findest du [hier](https://github.com/huggingface/course/blob/main/chapters/de/TRANSLATING.txt).
-
-
-- **Kann ich diesen Kurs auch an anderer Stelle verwenden?**
-Ja, natürlich! Der Kurs ist unter der permissiven [Apache-2-Lizenz](https://www.apache.org/licenses/LICENSE-2.0.html) veröffentlicht. Das bedeutet, dass du den Kurs in angemessener Weise erwähnen, einen Verweis zur Lizenz angeben und darauf hinweisen musst, wenn du Änderungen vorgenommen hast. Du kannst dies in jeder angemessenen Weise tun, allerdings nicht in einer Weise, die den Eindruck erweckt, dass der Lizenzgeber dich oder deine Nutzung unterstützt. Wenn du den Kurs zitieren möchtest, verwende bitte den folgenden BibTeX-Eintrag:
-
-```
-@misc{huggingfacecourse,
- author = {Hugging Face},
- title = {The Hugging Face Course, 2022},
- howpublished = "\url{https://huggingface.co/course}",
- year = {2022},
- note = "[Online; accessed ]"
-}
-```
-
-Bist du bereit, loszulegen? In diesem Kapitel lernst du
-* wie man die Funktion `pipeline()` benutzt, um computerlinguistische Aufgaben wie Textgenerierung und Klassifizierung zu lösen,
-* mehr über die Transformer-Architektur und
-* wie zwischen Encoder-, Decoder- und Encoder-Decoder-basierten Architekturen und -Anwendungsfällen unterschieden werden kann.
+# Einführung
+
+
+
+## Willkommen zum 🤗 Kurs!
+
+
+
+In diesem Kurs lernst du verschiedene Teilbereiche der maschinellen Verarbeitung natürlicher Sprache (engl. Natural Language Processing, NLP) - im Deutschen auch als Maschinelle Sprachverarbeitung oder Computerlinguistik (CL) bezeichnet - unter Verwendung der Bibliotheken des Ökosystems von [Hugging Face](https://huggingface.co/) kennen: die [🤗 Transformers-](https://github.com/huggingface/transformers), die [🤗 Datasets-](https://github.com/huggingface/datasets), die [🤗 Tokenizers-](https://github.com/huggingface/tokenizers) sowie die [🤗 Accelerate-Bibliotheken](https://github.com/huggingface/accelerate) als auch der [Hugging Face Hub](https://huggingface.co/models). Der Kurs ist komplett kostenlos und frei von Werbung.
+
+
+## Was erwartet dich?
+
+Hier ein kurzer Überblick über den Kurs:
+
+
+

+

+
+
+- Die Kapitel 1 bis 4 geben eine Einführung in die wichtigsten Konzepte der 🤗 Transformers-Bibliothek. Am Ende dieses Teils des Kurses wirst du mit der Funktionsweise von Transformer-Modellen vertraut sein und wissen, wie du ein Modell aus dem [Hugging Face Hub](https://huggingface.co/models) verwendest, es auf einem Datensatz feintunst und deine Ergebnisse mit anderen auf dem Hub teilst!
+- In den Kapiteln 5 bis 8 lernst du die Grundlagen der 🤗 Datasets- und 🤗 Tokenizers-Bibliotheken kennen, bevor du in die typischen Problemstellungen des NLP eintauchst. Am Ende dieses Teils wirst du in der Lage sein, die gängisten Problemstellungen im NLP selbstständig zu lösen.
+- Die Kapitel 9 bis 12 gehen über den Bereich des NLP hinaus und zeigen, wie Transformer-Modelle für Aufgaben bei der Verarbeitung gesprochener Sprache (engl. Speech Processing) und im Bereich Computer Vision (im Deutschen ungefähr mit computerbasiertem Sehen zu übersetzen) eingesetzt werden können. Nebenbei lernst du, wie du eigene Versionen deiner Modelle zu Demonstrationszwecken erstellen und sie mit anderen teilen kannst, und wie du sie für Produktionsumgebungen optimierst. Am Ende dieses Teils wirst du in der Lage sein, die 🤗 Transformers-Bibliothek auf (fast) jede Problemstellung, die dir im Bereich des Maschinellen Lernens begegnen, anzuwenden!
+
+Dieser Kurs:
+
+* Erfordert gute Kenntnisse in Python
+* Sollte am besten nach einem Einführungskurs in Deep Learning gemacht werden, wie z. B. [fast.ai's Kurs](https://www.fast.ai/) [Practical Deep Learning for Coders](https://course.fast.ai/) oder eines der von [DeepLearning.AI](https://www.deeplearning.ai/) entwickelten Kursprogramme
+* Setzt keine Vorkenntnisse in [PyTorch](https://pytorch.org/) oder [TensorFlow](https://www.tensorflow.org/) voraus, obwohl es hilfreich ist, wenn du bereits mit ihnen vertraut sein solltest.
+
+Nachdem du diesen Kurs abgeschlossen hast, empfehlen wir dir den [Spezialisierungskurs Natural Language Processing von DeepLearning.AI](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh), der eine breite Palette traditioneller NLP-Modelle wie Naive Bayes und LSTMs abdeckt, bei denen es sich lohnt, sich mit ihnen vertraut zu machen!
+
+## Wer sind wir?
+
+Über die Autorinnen und Autoren:
+
+**Matthew Carrigan** ist Machine Learning Engineer bei Hugging Face. Er lebt in der irischen Hauptstadt Dublin und hat zuvor als Machine Learning Engineer bei Parse.ly und als Post-Doktorand am Trinity College Dublin gearbeitet. Er glaubt nicht, dass wir eine künstliche allgemeine Intelligenz (engl. Artificial General Intelligence, AGI) durch eine zunehmende Skalierung bestehender Architekturen erreichen werden, hat aber dennoch die Hoffnung, dass Roboter auf dem Weg zur Unsterblichkeit sind.
+
+**Lysandre Debut** ist Machine Learning Engineer bei Hugging Face und arbeitet bereits seit Entstehung an der 🤗 Transformers-Bibliothek mit. Sein Ziel ist es, NLP für alle zugänglich zu machen, indem er Tools entwickelt, die eine sehr einfache API bieten.
+
+**Sylvain Gugger** ist Research Engineer bei Hugging Face und einer der Hauptverantwortlichen für die Pflege der 🤗 Transformers-Bibliothek. Zuvor war er Research Scientist bei fast.ai und hat zusammen mit Jeremy Howard das Buch _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ verfasst. Seine Forschung ist darauf ausgerichtet, Deep Learning zugänglicher zu machen. Hierfür entwickelt und verbessert er Techniken, mit denen Modelle auch bei begrenzter Ressourcenausstattung auf schnelle Weise trainiert werden können.
+
+**Merve Noyan** ist Developer Advocate bei Hugging Face und arbeitet daran, Tools zu entwickeln und Inhalte zu erstellen, die Maschinelles Lernen für jeden zugänglich machen.
+
+**Lucile Saulnier** ist Machine Learning Engineer bei Hugging Face und entwickelt und unterstützt die Nutzung von Open-Source-Tools. Außerdem ist sie aktiv an vielen Forschungsprojekten im Bereich des NLP beteiligt, z. B. an kollaborativem Training und BigScience.
+
+**Lewis Tunstall** ist Machine Learning Engineer bei Hugging Face, und konzentriert sich darauf, Open-Source-Tools zu entwickeln und sie der breiten Community zugänglich zu machen. Zudem ist er Mitverfasser des O'Reilly-Buches [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).
+
+**Leandro von Werra** ist Machine Learning Engineer im Open-Source-Team von Hugging Face und ebenfalls einer der Autoren des O'Reilly-Buches [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). Er hat mehrere Jahre praktische Erfahrung darin gesammelt, NLP-Projekte in die Produktion zu bringen, und dabei den gesamten ML-Stack beackert.
+
+## Häufig gestellte Fragen (FAQ)
+
+Hier findest du einige Antworten auf häufig gestellte Fragen:
+
+- **Erhalte ich für die Teilnahme an diesem Kurs ein Zertifikat?
+Derzeit gibt es für diesen Kurs noch kein Zertifikat. Wir arbeiten jedoch an einem Programm zur Erlangung eines Zertifikats für das Hugging-Face-Ökosystem - bleib' auf dem Laufenden!
+
+- **Wie viel Zeit sollte ich für diesen Kurs einplanen?
+Jedes Kapitel dieses Kurses ist so konzipiert, dass es innerhalb einer Woche abgeschlossen werden kann, wenn du circa 6 bis 8 Stunden Arbeit einplanst. Du kannst dir jedoch so viel Zeit nehmen wie nötig.
+
+- **Wo kann ich Fragen stellen, wenn ich welche habe?**
+Wenn du eine Frage zu einem Kursabschnitt hast, klicke einfach auf das sich oben auf der Seite befindende Banner "*Ask a question*" und du wirst automatisch zum entsprechenden Bereich des [Hugging-Face-Forums](https://discuss.huggingface.co/) weitergeleitet:
+
+
+
+Wenn du nach dem Kurs noch weiter üben möchtest, steht dir in den Foren eine Liste mit [Projektideen](https://discuss.huggingface.co/c/course/course-event/25) zur Verfügung.
+
+- **Wo finde ich den Code für den Kurs?**
+In jedem Abschnitt kannst du auf das oben auf der Seite befindliche Banner klicken, um den Code entweder in Google Colab oder in Amazon SageMaker Studio Lab auszuführen:
+
+
+
+Die Jupyter-Notebooks, die den gesamten Code des Kurses enthalten, befinden sich im [`huggingface/notebooks`-Repo](https://github.com/huggingface/notebooks). Wenn du sie lokal aufsetzen möchtest, schau dir die Anweisungen im [`course`-Repository](https://github.com/huggingface/course#-jupyter-notebooks) auf GitHub an.
+
+
+- **Wie kann ich etwas zum Kurs beitragen?**
+Es gibt mehrere Möglichkeiten, zum Kurs beizutragen! Wenn du einen Tippfehler oder einen Fehler entdeckst, eröffne bitte ein Issue in dem [`course`-Repository](https://github.com/huggingface/course). Wenn du uns dabei unterstützen möchtest, den Kurs in deine Muttersprache zu übersetzen, sieh dir bitte die [Anleitung](https://github.com/huggingface/course#translating-the-course-into-your-language) an.
+
+- **Welche Entscheidungen wurden bei den einzelnen Übersetzungen getroffen?**
+Für jede Übersetzung gibt es ein Glossar und die Datei `TRANSLATING.txt`, in der die gewählten Fachtermini usw. festgehalten sind. Ein Beispiel für die deutsche Fassung findest du [hier](https://github.com/huggingface/course/blob/main/chapters/de/TRANSLATING.txt).
+
+
+- **Kann ich diesen Kurs auch an anderer Stelle verwenden?**
+Ja, natürlich! Der Kurs ist unter der permissiven [Apache-2-Lizenz](https://www.apache.org/licenses/LICENSE-2.0.html) veröffentlicht. Das bedeutet, dass du den Kurs in angemessener Weise erwähnen, einen Verweis zur Lizenz angeben und darauf hinweisen musst, wenn du Änderungen vorgenommen hast. Du kannst dies in jeder angemessenen Weise tun, allerdings nicht in einer Weise, die den Eindruck erweckt, dass der Lizenzgeber dich oder deine Nutzung unterstützt. Wenn du den Kurs zitieren möchtest, verwende bitte den folgenden BibTeX-Eintrag:
+
+```
+@misc{huggingfacecourse,
+ author = {Hugging Face},
+ title = {The Hugging Face Course, 2022},
+ howpublished = "\url{https://huggingface.co/course}",
+ year = {2022},
+ note = "[Online; accessed ]"
+}
+```
+
+Bist du bereit, loszulegen? In diesem Kapitel lernst du
+* wie man die Funktion `pipeline()` benutzt, um computerlinguistische Aufgaben wie Textgenerierung und Klassifizierung zu lösen,
+* mehr über die Transformer-Architektur und
+* wie zwischen Encoder-, Decoder- und Encoder-Decoder-basierten Architekturen und -Anwendungsfällen unterschieden werden kann.
diff --git a/chapters/de/chapter1/10.mdx b/chapters/de/chapter1/10.mdx
index c6102e63e..977ec0b5f 100644
--- a/chapters/de/chapter1/10.mdx
+++ b/chapters/de/chapter1/10.mdx
@@ -1,260 +1,260 @@
-
-
-
-
-
-# Quiz am Ende des Kapitels
-
-In diesem Kapitel hast du viel gelernt! Mach dir keine Sorgen, wenn du noch nicht alle Einzelheiten verstanden hast. In den nächsten Kapiteln wirst du mehr darüber erfahren, wie die Dinge im Einzelnen funktionieren.
-
-Doch zuerst wollen wir noch testen, was du in diesem Kapitel gelernt hast!
-
-
-### 1. Erkunde den Hub und suche nach dem Checkpoint `roberta-large-mnli`. Welche Aufgabe unterstützt er?
-
-
-roberta-large-mnli nach."
- },
- {
- text: "Text Classification (Textklassifizierung)",
- explain: "Genauer gesagt, wird klassifiziert, ob zwei Sätze hinsichtlich dreier Labels (Widerspruch (engl. Contradiction), Neutral, Konsequenz (engl. Entailment)) logisch miteinander verbunden sind - eine Aufgabe, die auch als Natural Language Inference bezeichnet wird.",
- correct: true
- },
- {
- text: "Text Generation (Textgenerierung)",
- explain: "Sieh nochmal auf der Seite des Modells roberta-large-mnli nach."
- }
- ]}
-/>
-
-### 2. Was gibt der folgende Code zurück?
-
-```py
-from transformers import pipeline
-
-ner = pipeline("ner", aggregation_strategy="simple")
-ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
-```
-
-sentiment-analysis-Pipeline verwenden."
- },
- {
- text: "Er wird einen generierten Text zurückgeben, der diesen Satz vervollständigt.",
- explain: "Das ist nicht richtig - dafür müsstest du eine text-generation-Pipeline verwenden.",
- },
- {
- text: "Er gibt Begriffe zurück, die für Personen, Organisationen oder Orte stehen.",
- explain: "Außerdem werden mit aggregation_strategy="simple" die Wörter, die zur selben Entität gehören, gruppiert, wie z. B. \"Hugging Face\".",
- correct: true
- }
- ]}
-/>
-
-### 3. Wodurch müsste ... in diesem Codebeispiel ersetzt werden?
-
-```py
-from transformers import pipeline
-
-filler = pipeline("fill-mask", model="bert-base-cased")
-result = filler("...")
-```
-
- has been waiting for you.",
- explain: "Das stimmt nicht. Schau dir die bert-base-cased-Übersichtsseite des Modells an und versuche, deinen Fehler zu entdecken."
- },
- {
- text: "This [MASK] has been waiting for you.",
- explain: "Richtig! Der Mask Token dieses Modells ist [MASK].",
- correct: true
- },
- {
- text: "This man has been waiting for you.",
- explain: "Leider falsch. Diese Pipeline füllt maskierte Wörter auf, also braucht sie irgendwo einen Mask Token."
- }
- ]}
-/>
-
-### 4. Warum wird dieser Code nicht funktionieren?
-
-```py
-from transformers import pipeline
-
-classifier = pipeline("zero-shot-classification")
-result = classifier("This is a course about the Transformers library")
-```
-
-candidate_labels=[...] enthalten.",
- correct: true
- },
- {
- text: "Diese Pipeline erfordert mehrere Sätze, nicht nur einen.",
- explain: "Das ist falsch - obwohl diese Pipeline, wenn sie korrekt verwendet wird, eine Liste von Sätzen verarbeiten kann (wie alle anderen Pipelines)."
- },
- {
- text: "Die 🤗 Transformers-Bibliothek funktioniert wie immer nicht.",
- explain: "Zu dieser Antwort erübrigt sich jeder Kommentar!"
- },
- {
- text: "Diese Pipeline erfordert längere Inputs; diese hier sind zu kurz.",
- explain: "Das ist falsch. Übrigens wird ein sehr langer Text bei der Verarbeitung durch diese Pipeline gestutzt (engl. truncated) bzw. gekürzt."
- }
- ]}
-/>
-
-### 5. Was bedeutet der Begriff "Transfer Learning"?
-
-
-
-### 6. Richtig oder falsch? Ein Sprachmodell benötigt im Rahmen des Pretraining in der Regel keine Labels.
-
-
-selbstüberwacht (engl. self-supervised), d. h. die Labels werden automatisch aus den Inputs erstellt (wie z. B. die Vorhersage des nächsten Wortes oder das Auffüllen einiger maskierter Wörter).",
- correct: true
- },
- {
- text: "Falsch",
- explain: "Das ist nicht die richtige Antwort."
- }
- ]}
-/>
-
-### 7. Wähle den Satz aus, der die Begriffe "Modell", "Architektur" und "Gewichte" bzw. "Gewichtung" am besten beschreibt.
-
-
-
-
-### 8. Welche dieser Modelle würdest du nutzen, um einen Prompt bzw. Text-Input durch einen generierten Text vervollständigen zu lassen?
-
-
-
-### 9. Welche dieser Modelle würdest du für die Zusammenfassung von Texten verwenden?
-
-
-
-### 10. Welche Art von Modellen würdest du verwenden, um Text-Inputs entsprechend bestimmter Labels zu klassifizieren?
-
-
-
-### 11. Welche mögliche Ursache kann eine vom Modell zu beobachtende Voreingenommenheit (Bias) haben?
-
-
+
+
+
+
+
+# Quiz am Ende des Kapitels
+
+In diesem Kapitel hast du viel gelernt! Mach dir keine Sorgen, wenn du noch nicht alle Einzelheiten verstanden hast. In den nächsten Kapiteln wirst du mehr darüber erfahren, wie die Dinge im Einzelnen funktionieren.
+
+Doch zuerst wollen wir noch testen, was du in diesem Kapitel gelernt hast!
+
+
+### 1. Erkunde den Hub und suche nach dem Checkpoint `roberta-large-mnli`. Welche Aufgabe unterstützt er?
+
+
+roberta-large-mnli nach."
+ },
+ {
+ text: "Text Classification (Textklassifizierung)",
+ explain: "Genauer gesagt, wird klassifiziert, ob zwei Sätze hinsichtlich dreier Labels (Widerspruch (engl. Contradiction), Neutral, Konsequenz (engl. Entailment)) logisch miteinander verbunden sind - eine Aufgabe, die auch als Natural Language Inference bezeichnet wird.",
+ correct: true
+ },
+ {
+ text: "Text Generation (Textgenerierung)",
+ explain: "Sieh nochmal auf der Seite des Modells roberta-large-mnli nach."
+ }
+ ]}
+/>
+
+### 2. Was gibt der folgende Code zurück?
+
+```py
+from transformers import pipeline
+
+ner = pipeline("ner", aggregation_strategy="simple")
+ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+sentiment-analysis-Pipeline verwenden."
+ },
+ {
+ text: "Er wird einen generierten Text zurückgeben, der diesen Satz vervollständigt.",
+ explain: "Das ist nicht richtig - dafür müsstest du eine text-generation-Pipeline verwenden.",
+ },
+ {
+ text: "Er gibt Begriffe zurück, die für Personen, Organisationen oder Orte stehen.",
+ explain: "Außerdem werden mit aggregation_strategy="simple" die Wörter, die zur selben Entität gehören, gruppiert, wie z. B. \"Hugging Face\".",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. Wodurch müsste ... in diesem Codebeispiel ersetzt werden?
+
+```py
+from transformers import pipeline
+
+filler = pipeline("fill-mask", model="bert-base-cased")
+result = filler("...")
+```
+
+ has been waiting for you.",
+ explain: "Das stimmt nicht. Schau dir die bert-base-cased-Übersichtsseite des Modells an und versuche, deinen Fehler zu entdecken."
+ },
+ {
+ text: "This [MASK] has been waiting for you.",
+ explain: "Richtig! Der Mask Token dieses Modells ist [MASK].",
+ correct: true
+ },
+ {
+ text: "This man has been waiting for you.",
+ explain: "Leider falsch. Diese Pipeline füllt maskierte Wörter auf, also braucht sie irgendwo einen Mask Token."
+ }
+ ]}
+/>
+
+### 4. Warum wird dieser Code nicht funktionieren?
+
+```py
+from transformers import pipeline
+
+classifier = pipeline("zero-shot-classification")
+result = classifier("This is a course about the Transformers library")
+```
+
+candidate_labels=[...] enthalten.",
+ correct: true
+ },
+ {
+ text: "Diese Pipeline erfordert mehrere Sätze, nicht nur einen.",
+ explain: "Das ist falsch - obwohl diese Pipeline, wenn sie korrekt verwendet wird, eine Liste von Sätzen verarbeiten kann (wie alle anderen Pipelines)."
+ },
+ {
+ text: "Die 🤗 Transformers-Bibliothek funktioniert wie immer nicht.",
+ explain: "Zu dieser Antwort erübrigt sich jeder Kommentar!"
+ },
+ {
+ text: "Diese Pipeline erfordert längere Inputs; diese hier sind zu kurz.",
+ explain: "Das ist falsch. Übrigens wird ein sehr langer Text bei der Verarbeitung durch diese Pipeline gestutzt (engl. truncated) bzw. gekürzt."
+ }
+ ]}
+/>
+
+### 5. Was bedeutet der Begriff "Transfer Learning"?
+
+
+
+### 6. Richtig oder falsch? Ein Sprachmodell benötigt im Rahmen des Pretraining in der Regel keine Labels.
+
+
+selbstüberwacht (engl. self-supervised), d. h. die Labels werden automatisch aus den Inputs erstellt (wie z. B. die Vorhersage des nächsten Wortes oder das Auffüllen einiger maskierter Wörter).",
+ correct: true
+ },
+ {
+ text: "Falsch",
+ explain: "Das ist nicht die richtige Antwort."
+ }
+ ]}
+/>
+
+### 7. Wähle den Satz aus, der die Begriffe "Modell", "Architektur" und "Gewichte" bzw. "Gewichtung" am besten beschreibt.
+
+
+
+
+### 8. Welche dieser Modelle würdest du nutzen, um einen Prompt bzw. Text-Input durch einen generierten Text vervollständigen zu lassen?
+
+
+
+### 9. Welche dieser Modelle würdest du für die Zusammenfassung von Texten verwenden?
+
+
+
+### 10. Welche Art von Modellen würdest du verwenden, um Text-Inputs entsprechend bestimmter Labels zu klassifizieren?
+
+
+
+### 11. Welche mögliche Ursache kann eine vom Modell zu beobachtende Voreingenommenheit (Bias) haben?
+
+
diff --git a/chapters/de/chapter1/2.mdx b/chapters/de/chapter1/2.mdx
index 9f36f8315..99a9d7e9f 100644
--- a/chapters/de/chapter1/2.mdx
+++ b/chapters/de/chapter1/2.mdx
@@ -1,26 +1,26 @@
-# Computerlinguistik
-
-
-
-Bevor wir uns mit Transformer-Modellen beschäftigen, wollen wir dir einen kurzen Überblick darüber geben, was Computerlinguistik (engl. Natural Language Processing, NLP) ist und welche Gründe es gibt, sich damit zu befassen.
-
-## Was ist Computerlinguistik (CL)?
-
-CL ist ein Bereich der Linguistik und des Maschinellen Lernens (engl. Machine Learning, ML), der sich darauf konzentriert, alle mit menschlicher Sprache zusammenhängenden Dinge zu verstehen. Das Ziel bei CL-Aufgabenstellungen (engl. Tasks) ist es, nicht nur einzelne Wörter zu verstehen, sondern auch den Kontext dieser Wörter zu erfassen.
-
-Im Folgenden findest du eine Liste der häufigsten CL-Aufgabenstellungen mit jeweils einigen Beispielen:
-
-- **Ganze Sätze klassifizieren**: Die mit einer bestimmten Bewertung verbundene Stimmungslage ermitteln, erkennen, ob eine E-Mail Spam ist, bestimmen, ob ein Satz grammatikalisch korrekt ist oder ob zwei Sätze logisch zusammenhängen oder nicht
-- **Jedes einzelne Wort in einem Satz klassifizieren**: Identifizieren der grammatikalischen Bestandteile eines Satzes (Substantiv, Verb, Adjektiv) oder der benannten Entitäten (Person, Ort, Organisation) (engl. Named Entities)
-- **Generieren von Textinhalten**: Einen Prompt durch einen automatisch generierten Text vervollständigen oder Lücken in einem Text auffüllen, in dem einzelne Wörter maskiert sind
-- **Eine Antwort aus einem Text extrahieren**: Auf Basis einer Frage und eines gegebenen Kontexts die Antwort auf die Frage anhand der im Kontext enthaltenen Informationen extrahieren
-- **Generieren eines neuen Satzes auf Basis eines Input-Textes**: Einen Text in eine andere Sprache automatisch übersetzen, Zusammenfassen eines Textes
-
-Die Computerlinguistik ist jedoch nicht nur auf die Verarbeitung geschriebener Texte beschränkt. Sie stellt sich auch komplexen Herausforderungen in den Bereichen der Spracherkennung (engl. Speech Recognition) und Computer Vision, wie z. B. ein Transkript einer Audioaufnahme zu erstellen oder ein Bild zu beschreiben.
-
-## Warum ist Computerlinguistik so schwierig?
-
-Computer verarbeiten Informationen nicht auf die gleiche Weise wie Menschen. Wenn wir zum Beispiel den Satz "Ich bin hungrig" lesen, können wir seine Bedeutung leicht erfassen. Genauso können wir bei zwei Sätzen wie "Ich habe Hunger" und "Ich bin traurig" leicht feststellen, wie ähnlich sie sind. Für ML-Modelle sind solche Aufgaben schwieriger zu lösen. Der Text muss erst so verarbeitet werden, dass das Modell in der Lage ist, daraus zu lernen. Und weil Sprache komplex ist, müssen wir uns genau überlegen, wie diese Verarbeitung erfolgen sollte. Es wurde eine rege Forschung dazu betrieben, wie Texte repräsentiert werden können. Einige dieser Methoden werden wir uns im nächsten Kapitel ansehen.
+# Computerlinguistik
+
+
+
+Bevor wir uns mit Transformer-Modellen beschäftigen, wollen wir dir einen kurzen Überblick darüber geben, was Computerlinguistik (engl. Natural Language Processing, NLP) ist und welche Gründe es gibt, sich damit zu befassen.
+
+## Was ist Computerlinguistik (CL)?
+
+CL ist ein Bereich der Linguistik und des Maschinellen Lernens (engl. Machine Learning, ML), der sich darauf konzentriert, alle mit menschlicher Sprache zusammenhängenden Dinge zu verstehen. Das Ziel bei CL-Aufgabenstellungen (engl. Tasks) ist es, nicht nur einzelne Wörter zu verstehen, sondern auch den Kontext dieser Wörter zu erfassen.
+
+Im Folgenden findest du eine Liste der häufigsten CL-Aufgabenstellungen mit jeweils einigen Beispielen:
+
+- **Ganze Sätze klassifizieren**: Die mit einer bestimmten Bewertung verbundene Stimmungslage ermitteln, erkennen, ob eine E-Mail Spam ist, bestimmen, ob ein Satz grammatikalisch korrekt ist oder ob zwei Sätze logisch zusammenhängen oder nicht
+- **Jedes einzelne Wort in einem Satz klassifizieren**: Identifizieren der grammatikalischen Bestandteile eines Satzes (Substantiv, Verb, Adjektiv) oder der benannten Entitäten (Person, Ort, Organisation) (engl. Named Entities)
+- **Generieren von Textinhalten**: Einen Prompt durch einen automatisch generierten Text vervollständigen oder Lücken in einem Text auffüllen, in dem einzelne Wörter maskiert sind
+- **Eine Antwort aus einem Text extrahieren**: Auf Basis einer Frage und eines gegebenen Kontexts die Antwort auf die Frage anhand der im Kontext enthaltenen Informationen extrahieren
+- **Generieren eines neuen Satzes auf Basis eines Input-Textes**: Einen Text in eine andere Sprache automatisch übersetzen, Zusammenfassen eines Textes
+
+Die Computerlinguistik ist jedoch nicht nur auf die Verarbeitung geschriebener Texte beschränkt. Sie stellt sich auch komplexen Herausforderungen in den Bereichen der Spracherkennung (engl. Speech Recognition) und Computer Vision, wie z. B. ein Transkript einer Audioaufnahme zu erstellen oder ein Bild zu beschreiben.
+
+## Warum ist Computerlinguistik so schwierig?
+
+Computer verarbeiten Informationen nicht auf die gleiche Weise wie Menschen. Wenn wir zum Beispiel den Satz "Ich bin hungrig" lesen, können wir seine Bedeutung leicht erfassen. Genauso können wir bei zwei Sätzen wie "Ich habe Hunger" und "Ich bin traurig" leicht feststellen, wie ähnlich sie sind. Für ML-Modelle sind solche Aufgaben schwieriger zu lösen. Der Text muss erst so verarbeitet werden, dass das Modell in der Lage ist, daraus zu lernen. Und weil Sprache komplex ist, müssen wir uns genau überlegen, wie diese Verarbeitung erfolgen sollte. Es wurde eine rege Forschung dazu betrieben, wie Texte repräsentiert werden können. Einige dieser Methoden werden wir uns im nächsten Kapitel ansehen.
diff --git a/chapters/de/chapter1/3.mdx b/chapters/de/chapter1/3.mdx
index 1e2c509d5..f3df13086 100644
--- a/chapters/de/chapter1/3.mdx
+++ b/chapters/de/chapter1/3.mdx
@@ -249,8 +249,7 @@ Bei der automatischen Textzusammenfassung (engl. Summarization) geht es darum, e
from transformers import pipeline
summarizer = pipeline("summarization")
-summarizer(
- """
+summarizer("""
America has changed dramatically during recent years. Not only has the number of
graduates in traditional engineering disciplines such as mechanical, civil,
electrical, chemical, and aeronautical engineering declined, but in most of
@@ -269,8 +268,7 @@ summarizer(
Other industrial countries at minimum maintain their output, while America
suffers an increasingly serious decline in the number of engineering graduates
and a lack of well-educated engineers.
-"""
-)
+""")
```
```python out
diff --git a/chapters/de/chapter1/4.mdx b/chapters/de/chapter1/4.mdx
index c9641aa31..006248ba1 100644
--- a/chapters/de/chapter1/4.mdx
+++ b/chapters/de/chapter1/4.mdx
@@ -1,176 +1,176 @@
-# Wie funktionieren Transformer-Modelle?
-
-
-
-In diesem Abschnitt werfen wir einen Blick auf die Architektur von Transformer-Modellen.
-
-## Kurz zur Entwicklungsgeschichte der Transformer-Modelle
-
-Hier sind einige wichtige Meilensteine in der (kurzen) Geschichte der Transformer-Modelle:
-
-
-

-

-
-
-Die [Transformer-Architektur](https://arxiv.org/abs/1706.03762) wurde erstmals im Juni 2017 veröffentlicht. Der Schwerpunkt der ursprünglichen Forschung lag auf Übersetzungsaufgaben. In der Folge wurden mehrere einflussreiche Modelle veröffentlicht, darunter:
-
-- **Juni 2018**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), das erste vortrainierte Transformer-Modell, wurde zum Feintuning für verschiedene CL-Aufgaben eingesetzt und erzielte Ergebnisse, die dem neuesten Stand der Technik entsprachen.
-
-- **Oktober 2018**: [BERT](https://arxiv.org/abs/1810.04805), ein weiteres großes vortrainiertes Modell, das dazu dient, bessere Zusammenfassungen von Sätzen zu erstellen (mehr dazu im nächsten Kapitel!)
-
-- **Februar 2019**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), eine verbesserte (und größere) Version von GPT, die aus ethischen Erwägungen nicht sofort veröffentlicht wurde
-
-- **Oktober 2019**: [DistilBERT](https://arxiv.org/abs/1910.01108), eine abgespeckte Version von BERT, die 60 % schneller ist, 40 % weniger Speicherplatz benötigt und dennoch 97 % der Leistung von BERT erreicht
-
-- **Oktober 2019**: [BART](https://arxiv.org/abs/1910.13461) und [T5](https://arxiv.org/abs/1910.10683), zwei große vortrainierte Modelle, die dieselbe Architektur wie das ursprüngliche Transformer-Modell verwenden (die ersten, die dies getan haben)
-
-- **Mai 2020**, [GPT-3](https://arxiv.org/abs/2005.14165), eine noch größere Version von GPT-2, die in der Lage ist, bei einer Vielzahl von Aufgabenstellungen gute Leistungen zu erbringen, ohne dass ein Feintuning erforderlich ist (auch _Zero-Shot Learning_ genannt)
-
-Diese Auflistung ist bei weitem nicht vollständig und soll nur einige der verschiedenen Arten von Transformer-Modellen aufzeigen. Sie lassen sich grob in drei Kategorien einteilen:
-
-- GPT-ähnliche (auch _autoregressive_-Transformer-Modelle genannt)
-- BERT-ähnliche (auch _Auto-Encoding_-Transformer-Modelle genannt)
-- BART-/T5-ähnliche (auch _Sequence-to-Sequence_-Transformer-Modelle genannt)
-
-Wir werden uns mit diesen unterschiedlichen Modellfamilien später noch eingehender beschäftigen.
-
-## Transformer-Modelle sind Sprachmodelle
-
-Alle oben genannten Transformer-Modelle (GPT, BERT, BART, T5, etc.) wurden als *Sprachmodelle* (engl. Language Models) trainiert. Das bedeutet, dass sie mit großen Mengen an Rohtext auf selbstüberwachte (engl. self-supervised) Weise trainiert wurden. Selbstüberwachtes Lernen ist eine Form des Trainings, bei der die vorherzusagende Variable, die sog. Zielvariable (engl. Target), automatisch aus den Inputs des Modells berechnet wird. Das bedeutet, dass kein menschliches Zutun nötig ist, um die Daten zu labeln!
-
-Diese Art von Modell entwickelt ein statistisches Verständnis der Sprache, auf die es trainiert wurde, ist aber für spezifische praktische Aufgaben nicht sehr nützlich. Aus diesem Grund durchläuft das allgemeine, vortrainierte Modell ein Vorgang namens *Transfer Learning*. Während dieses Vorgangs wird das Modell unter Überwachung - d. h. mit Hilfe von durch Menschen bereitgestellte Labels - für eine bestimmte Aufgabe feingetunt.
-
-Ein Beispiel für eine Aufgabe ist die Vorhersage des nächsten Wortes in einem Satz, nachdem man die *n* vorherigen Wörter gelesen hat. Dies nennt sich *kausale Sprachmodellierung* (engl. Causal Language Modeling), da der Output von den vergangenen und aktuellen Inputs abhängt, aber nicht von den zukünftigen.
-
-
-

-

-
-
-Ein weiteres Beispiel ist die *maskierte Sprachmodellierung* (engl. Masked Language Modeling), bei der das Modell ein Wort im Satz, das maskiert ist, vorhersagt.
-
-
-

-

-
-
-## Transformer-Modelle sind groß
-
-Abgesehen von einigen wenigen Ausreißern (wie DistilBERT) besteht die allgemeine Strategie, um eine bessere Leistung zu erzielen, darin, die Modelle zu vergrößern und die Menge an Daten zu erhöhen, auf denen sie vortrainiert werden.
-
-
-

-
-
-Leider erfordert das Training eines Modells, insbesondere eines großen, eine große Menge an Daten. Das ist sehr kostspielig in Bezug auf Zeit und Rechenleistung. Es hat sogar Auswirkungen auf die Umwelt, wie in der folgenden Grafik zu sehen ist.
-
-
-

-

-
-
-
-
-Hier ist ein Projekt zu sehen, bei dem ein Team gezielt versucht, die Umweltauswirkungen des Pretrainings (sehr großer) Modelle zu reduzieren. Wenn man die vielen Versuche berücksichtigt, die dazu nötig sind, die besten Hyperparameter zu finden, wären die zu bemessenden ökologischen Konsequenzen noch größer.
-
-Stell dir vor, dass jedes Mal, wenn ein Forschungsteam, eine Bildungseinrichtung oder ein Unternehmen ein Modell trainieren möchte, dies von Grund auf tun müsste. Das würde zu enormen, unnötigen globalen Kosten führen!
-
-Deshalb ist die gemeinsame Nutzung von Sprachmodellen von größter Bedeutung: trainierte Gewichtungen gemeinsam zu nutzen und auf bereits trainierten Gewichtungen aufzubauen, reduziert die gesamten Rechenkosten und den CO2-Fußabdruck der Community.
-
-
-## Transfer Learning
-
-
-
-Beim *Pretraining* wird ein Modell von Grund auf neu trainiert: Die Gewichte werden nach dem Zufallsprinzip initialisiert und das Training beginnt ohne jegliches Vorwissen.
-
-
-

-

-
-
-Dieses Pretraining wird normalerweise mit sehr großen Datenmengen durchgeführt. Daher wird ein sehr großer Korpus an Daten benötigt und das Training kann mehrere Wochen in Anspruch nehmen.
-
-*Feintuning* ist hingegen das Training, das **nach** dem Pretraining eines Modells durchgeführt wird. Für das Feintuning nimmst du zunächst ein vortrainiertes Sprachmodell und trainierst es dann mit einem aufgabenspezifischen Datensatz nach. Moment - warum trainierst du das Modell nicht gleich für die endgültige Aufgabe? Dafür gibt es mehrere Gründe:
-
-* Das vortrainierte Modell wurde bereits auf einem Datensatz trainiert, der einige Ähnlichkeiten mit dem Datensatz, der für das Feintuning verwendet wird, aufweist. Beim Feintuning kann also von dem Wissen profitiert werden, das das ursprüngliche Modell während des Pretrainings erlangt hat (bei CL-Problemstellungen verfügt das vortrainierte Modell zum Beispiel über eine Art statistisches Verständnis der Sprache, die du für deine Aufgabe verwendest).
-* Da das vortrainierte Modell bereits auf vielen Daten trainiert wurde, sind zum Feintuning bedeutend weniger Daten erforderlich, um brauchbare Ergebnisse erzielen zu können.
-* Aus demselben Grund sind der Zeitaufwand und die Ressourcen, die für gute Ergebnisse benötigt werden, bedeutend geringer.
-
-Man könnte zum Beispiel ein auf Englisch trainiertes Modell nutzen und es dann auf einem arXiv-Korpus feintunen, um ein auf wissenschaftliche Sprache ausgerichtetes Modell zu erstellen. Für das Feintuning wird nur eine begrenzte Menge an Daten benötigt: Das Wissen, das das vortrainierte Modell erworben hat, wird "übertragen" (engl. transferred), daher der Begriff *Transfer Learning*.
-
-
-

-

-
-
-Das Feintuning eines Modells ist daher mit geringeren Zeit-, Daten-, Umwelt- und finanziellen Kosten verbunden. Es ist auch schneller und einfacher, verschiedene Modelle für das Feintuning auszuprobieren, da das Training mit geringeren Einschränkungen einhergeht als ein vollständiges Pretraining.
-
-Dieser Ansatz führt auch zu besseren Ergebnissen als ein Training von Grund auf (es sei denn, du hast viele Daten). Deshalb solltest du immer versuchen, ein vortrainiertes Modell zu nutzen - und zwar ein Modell, das so nah wie möglich an deiner Aufgabenstellung ist - und es für das Feintuning verwenden.
-
-## Grundlegende Architektur
-
-In diesem Abschnitt gehen wir auf die grundlegende Architektur des Transformer-Modells ein. Mach dir keine Sorgen, wenn du einige der Konzepte nicht verstehst. Im weiteren Verlauf folgen noch ausführliche Abschnitte zu den einzelnen Komponenten.
-
-
-
-## Einführung
-
-Das Modell besteht hauptsächlich aus zwei Blöcken:
-
-* **Encoder (links)**: Der Encoder, auch Kodierer genannt, empfängt einen Input und erstellt eine numerische Darstellung bzw. Repräsentation des Inputs (seiner Features, im Deutschen auch als Merkmale bezeichnet). Das bedeutet, dass das Modell darauf optimiert ist, ein Verständnis vom Input zu erlangen.
-* **Decoder (rechts)**: Der Decoder, auch bekannt als Dekodierer, verwendet die Repräsentation des Encoders (Features) zusammen mit anderen Inputs, um eine Zielsequenz zu generieren. Das bedeutet, dass das Modell darauf optimiert ist, einen Output zu generieren.
-
-
-

-

-
-
-Jede dieser Komponenten kann je nach Aufgabe unabhängig voneinander verwendet werden:
-
-* **Rein Encoder-basierte Modelle** ("Encoder-only Models"): Gut für Aufgaben, die ein Verständnis des Inputs erfordern, wie z. B. bei der Klassifizierung von Sätzen und der Eigennamenerkennung (NER).
-* **Rein Decoder-basierte Modelle** ("Decoder-only Models"): Gut geeignet für generative Aufgaben wie die Textgenerierung.
-* **Encoder-Decoder-basierte Modelle** bzw. **Sequence-to-Sequence-Modelle**: Gut für generative Aufgaben, die einen Input erfordern, wie z. B. Übersetzungen oder Zusammenfassungen.
-
-Wir werden diese Architekturen in späteren Abschnitten noch gesondert behandeln.
-
-## Attention-Layer
-
-Ein wesentliches Merkmal der Transformer-Modelle ist, dass sie mit speziellen Layern (im Deutschen auch als Schichten bezeichnet), den *Attention-Layern*, aufgebaut sind. Der Titel des Forschungsbeitrags, in dem die Transformer-Architektur vorgestellt wurde, lautete sogar ["Attention Is All You Need"](https://arxiv.org/abs/1706.03762)! Wir werden uns später im Kurs mit den Details von Attention-Layern befassen. Für den Moment musst du nur wissen, dass dieser Layer dem Modell sagt, dass es bei der Repräsentation eines jeden Worts in einem Satz, den du ihm übergeben hast, bestimmten Wörtern besondere Aufmerksamkeit schenken (und die anderen mehr oder weniger ignorieren) soll.
-
-Angenommen, du sollst einen Text aus dem Englischen ins Französische übersetzen. Bei dem Input "You like this course" muss ein Übersetzungsmodell auch das angrenzende Wort "You" berücksichtigen, um die richtige Übersetzung für das Wort "like" zu erhalten, denn im Französischen wird das Verb "like" je nach Subjekt unterschiedlich konjugiert. Der Rest des Satzes ist jedoch für die Übersetzung dieses Wortes nicht hilfreich. Genauso muss das Modell bei der Übersetzung von "this" auf das Wort "course" achten, denn "this" wird unterschiedlich übersetzt, je nachdem, ob das zugehörige Substantiv männlich oder weiblich ist. Auch hier spielen die anderen Wörter im Satz für die Übersetzung von "this" keine Rolle. Bei komplexeren Sätzen (und komplexeren Grammatikregeln) muss das Modell besonders auf Wörter achten, die weiter entfernt im Satz vorkommen, um jedes Wort richtig zu übersetzen.
-
-Das gleiche Konzept gilt für jede Aufgabenstellung, die mit natürlicher Sprache zu tun hat: Ein Wort an sich hat eine Bedeutung, aber diese Bedeutung hängt stark vom Kontext ab, der sich durch ein anderes Wort (oder Wörter) vor oder nach dem untersuchten Wort ergibt.
-
-Nachdem du nun eine Vorstellung davon hast, worum es bei Attention-Layern geht, nehmen wir die Transformer-Architektur genauer unter die Lupe.
-
-## Die ursprüngliche Architektur
-
-Die Transformer-Architektur wurde ursprünglich für die maschinelle Übersetzung entwickelt. Beim Training erhält der Encoder Inputs (Sätze) in einer bestimmten Sprache, während der Decoder die gleichen Sätze in der gewünschten Zielsprache erhält. Im Encoder können die Attention-Layer alle Wörter eines Satzes verwenden (denn wie wir gerade gesehen haben, kann die Übersetzung eines bestimmten Wortes sowohl von dem abhängen, was nach, als auch von dem, was vor dem Wort im Satz steht). Der Decoder arbeitet hingegen sequentiell und kann nur die Wörter im Satz berücksichtigen, die er bereits übersetzt hat (also nur die Wörter vor dem Wort, das gerade generiert wird). Wenn wir zum Beispiel die ersten drei Wörter der übersetzten Zielsequenz vorhergesagt haben, geben wir sie an den Decoder weiter, der dann alle Inputs des Encoders verwendet, um das vierte Wort vorherzusagen.
-
-Um das Training zu beschleunigen (insofern das Modell Zugriff auf die Zielsätze hat), wird der Decoder mit dem gesamten (vorherzusagenden) Zielsatz gefüttert, aber er darf keine nachfolgenden Wörter verwenden (wenn er Zugriff zum Wort an Position 2 hätte, während er versucht, das Wort an Position 2 vorherzusagen, wäre die Aufgabe nicht sonderlich schwer!). Wenn er zum Beispiel versucht, das vierte Wort vorherzusagen, hat der Attention-Layer nur Zugriff zu den Wörtern an den Positionen 1 bis 3.
-
-Die ursprüngliche Transformer-Architektur sah wie folgt aus - mit dem Encoder auf der linken und dem Decoder auf der rechten Seite:
-
-
-

-

-
-
-Beachte, dass die Attention des ersten Attention-Layers in einem Decoder-Block alle (vorangegangenen) Inputs, die der Decoder erhalten hat, berücksichtigt, während der zweite Attention-Layer den Output des Encoders verwendet. Im Rahmen der Vorhersage des aktuellen Wortes kann er also auf den gesamten Input-Satz zugreifen. Das ist vor allem deshalb nützlich, da es in den verschiedenen Sprachen unterschiedliche grammatikalische Regeln geben kann, wodurch die Wörter in einer anderen Reihenfolge aneinandergereiht werden. Ebenso könnte ein erst später im Satz enthaltener Zusammenhang dabei hilfreich sein, die beste Übersetzung eines bestimmten Wortes zu bestimmen.
-
-Die *Attention-Mask* kann auch im Encoder bzw. Decoder verwendet werden, um zu verhindern, dass das Modell bestimmte Wörter beachtet - zum Beispiel das spezielle Füllwort (engl. Padding Word), das verwendet wird, um alle Inputs auf die gleiche Länge zu bringen, wenn die Sätze zu Batches zusammengeführt werden.
-
-## Architekturen vs. Checkpoints
-
-Wenn wir uns in diesem Kurs mit Transformer-Modellen beschäftigen, wirst du auf *Architekturen*, *Checkpoints* und auch auf *Modelle* stoßen. Diese Begriffe haben alle eine etwas unterschiedliche Bedeutung:
-
-* **Architektur**: Dies ist das Skelett des Modells - die Definition jedes Layers und jeder Operation, die innerhalb des Modells stattfindet.
-* **Checkpoints**: Dies ist die Gewichtung, die für eine bestimmte Architektur geladen wird.
-* **Modell**: Dies ist ein Oberbegriff, der nicht so präzise ist wie "Architektur" oder "Checkpoint": Er kann beides bedeuten. In diesem Kurs wird jeweils explizit spezifiziert, ob es sich um eine *Architektur* oder um einen *Checkpoint* handelt, um Zweideutigkeiten zu vermeiden.
-
-BERT ist zum Beispiel eine Architektur, während `bert-base-cased` - ein Satz von Gewichten, der vom Google-Team für die erste Version von BERT trainiert wurde - ein Checkpoint ist. Man kann aber auch "das BERT-Modell" oder "das `bert-base-cased`-Modell" sagen.
+# Wie funktionieren Transformer-Modelle?
+
+
+
+In diesem Abschnitt werfen wir einen Blick auf die Architektur von Transformer-Modellen.
+
+## Kurz zur Entwicklungsgeschichte der Transformer-Modelle
+
+Hier sind einige wichtige Meilensteine in der (kurzen) Geschichte der Transformer-Modelle:
+
+
+

+

+
+
+Die [Transformer-Architektur](https://arxiv.org/abs/1706.03762) wurde erstmals im Juni 2017 veröffentlicht. Der Schwerpunkt der ursprünglichen Forschung lag auf Übersetzungsaufgaben. In der Folge wurden mehrere einflussreiche Modelle veröffentlicht, darunter:
+
+- **Juni 2018**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), das erste vortrainierte Transformer-Modell, wurde zum Feintuning für verschiedene CL-Aufgaben eingesetzt und erzielte Ergebnisse, die dem neuesten Stand der Technik entsprachen.
+
+- **Oktober 2018**: [BERT](https://arxiv.org/abs/1810.04805), ein weiteres großes vortrainiertes Modell, das dazu dient, bessere Zusammenfassungen von Sätzen zu erstellen (mehr dazu im nächsten Kapitel!)
+
+- **Februar 2019**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), eine verbesserte (und größere) Version von GPT, die aus ethischen Erwägungen nicht sofort veröffentlicht wurde
+
+- **Oktober 2019**: [DistilBERT](https://arxiv.org/abs/1910.01108), eine abgespeckte Version von BERT, die 60 % schneller ist, 40 % weniger Speicherplatz benötigt und dennoch 97 % der Leistung von BERT erreicht
+
+- **Oktober 2019**: [BART](https://arxiv.org/abs/1910.13461) und [T5](https://arxiv.org/abs/1910.10683), zwei große vortrainierte Modelle, die dieselbe Architektur wie das ursprüngliche Transformer-Modell verwenden (die ersten, die dies getan haben)
+
+- **Mai 2020**, [GPT-3](https://arxiv.org/abs/2005.14165), eine noch größere Version von GPT-2, die in der Lage ist, bei einer Vielzahl von Aufgabenstellungen gute Leistungen zu erbringen, ohne dass ein Feintuning erforderlich ist (auch _Zero-Shot Learning_ genannt)
+
+Diese Auflistung ist bei weitem nicht vollständig und soll nur einige der verschiedenen Arten von Transformer-Modellen aufzeigen. Sie lassen sich grob in drei Kategorien einteilen:
+
+- GPT-ähnliche (auch _autoregressive_-Transformer-Modelle genannt)
+- BERT-ähnliche (auch _Auto-Encoding_-Transformer-Modelle genannt)
+- BART-/T5-ähnliche (auch _Sequence-to-Sequence_-Transformer-Modelle genannt)
+
+Wir werden uns mit diesen unterschiedlichen Modellfamilien später noch eingehender beschäftigen.
+
+## Transformer-Modelle sind Sprachmodelle
+
+Alle oben genannten Transformer-Modelle (GPT, BERT, BART, T5, etc.) wurden als *Sprachmodelle* (engl. Language Models) trainiert. Das bedeutet, dass sie mit großen Mengen an Rohtext auf selbstüberwachte (engl. self-supervised) Weise trainiert wurden. Selbstüberwachtes Lernen ist eine Form des Trainings, bei der die vorherzusagende Variable, die sog. Zielvariable (engl. Target), automatisch aus den Inputs des Modells berechnet wird. Das bedeutet, dass kein menschliches Zutun nötig ist, um die Daten zu labeln!
+
+Diese Art von Modell entwickelt ein statistisches Verständnis der Sprache, auf die es trainiert wurde, ist aber für spezifische praktische Aufgaben nicht sehr nützlich. Aus diesem Grund durchläuft das allgemeine, vortrainierte Modell ein Vorgang namens *Transfer Learning*. Während dieses Vorgangs wird das Modell unter Überwachung - d. h. mit Hilfe von durch Menschen bereitgestellte Labels - für eine bestimmte Aufgabe feingetunt.
+
+Ein Beispiel für eine Aufgabe ist die Vorhersage des nächsten Wortes in einem Satz, nachdem man die *n* vorherigen Wörter gelesen hat. Dies nennt sich *kausale Sprachmodellierung* (engl. Causal Language Modeling), da der Output von den vergangenen und aktuellen Inputs abhängt, aber nicht von den zukünftigen.
+
+
+

+

+
+
+Ein weiteres Beispiel ist die *maskierte Sprachmodellierung* (engl. Masked Language Modeling), bei der das Modell ein Wort im Satz, das maskiert ist, vorhersagt.
+
+
+

+

+
+
+## Transformer-Modelle sind groß
+
+Abgesehen von einigen wenigen Ausreißern (wie DistilBERT) besteht die allgemeine Strategie, um eine bessere Leistung zu erzielen, darin, die Modelle zu vergrößern und die Menge an Daten zu erhöhen, auf denen sie vortrainiert werden.
+
+
+

+
+
+Leider erfordert das Training eines Modells, insbesondere eines großen, eine große Menge an Daten. Das ist sehr kostspielig in Bezug auf Zeit und Rechenleistung. Es hat sogar Auswirkungen auf die Umwelt, wie in der folgenden Grafik zu sehen ist.
+
+
+

+

+
+
+
+
+Hier ist ein Projekt zu sehen, bei dem ein Team gezielt versucht, die Umweltauswirkungen des Pretrainings (sehr großer) Modelle zu reduzieren. Wenn man die vielen Versuche berücksichtigt, die dazu nötig sind, die besten Hyperparameter zu finden, wären die zu bemessenden ökologischen Konsequenzen noch größer.
+
+Stell dir vor, dass jedes Mal, wenn ein Forschungsteam, eine Bildungseinrichtung oder ein Unternehmen ein Modell trainieren möchte, dies von Grund auf tun müsste. Das würde zu enormen, unnötigen globalen Kosten führen!
+
+Deshalb ist die gemeinsame Nutzung von Sprachmodellen von größter Bedeutung: trainierte Gewichtungen gemeinsam zu nutzen und auf bereits trainierten Gewichtungen aufzubauen, reduziert die gesamten Rechenkosten und den CO2-Fußabdruck der Community.
+
+
+## Transfer Learning
+
+
+
+Beim *Pretraining* wird ein Modell von Grund auf neu trainiert: Die Gewichte werden nach dem Zufallsprinzip initialisiert und das Training beginnt ohne jegliches Vorwissen.
+
+
+

+

+
+
+Dieses Pretraining wird normalerweise mit sehr großen Datenmengen durchgeführt. Daher wird ein sehr großer Korpus an Daten benötigt und das Training kann mehrere Wochen in Anspruch nehmen.
+
+*Feintuning* ist hingegen das Training, das **nach** dem Pretraining eines Modells durchgeführt wird. Für das Feintuning nimmst du zunächst ein vortrainiertes Sprachmodell und trainierst es dann mit einem aufgabenspezifischen Datensatz nach. Moment - warum trainierst du das Modell nicht gleich für die endgültige Aufgabe? Dafür gibt es mehrere Gründe:
+
+* Das vortrainierte Modell wurde bereits auf einem Datensatz trainiert, der einige Ähnlichkeiten mit dem Datensatz, der für das Feintuning verwendet wird, aufweist. Beim Feintuning kann also von dem Wissen profitiert werden, das das ursprüngliche Modell während des Pretrainings erlangt hat (bei CL-Problemstellungen verfügt das vortrainierte Modell zum Beispiel über eine Art statistisches Verständnis der Sprache, die du für deine Aufgabe verwendest).
+* Da das vortrainierte Modell bereits auf vielen Daten trainiert wurde, sind zum Feintuning bedeutend weniger Daten erforderlich, um brauchbare Ergebnisse erzielen zu können.
+* Aus demselben Grund sind der Zeitaufwand und die Ressourcen, die für gute Ergebnisse benötigt werden, bedeutend geringer.
+
+Man könnte zum Beispiel ein auf Englisch trainiertes Modell nutzen und es dann auf einem arXiv-Korpus feintunen, um ein auf wissenschaftliche Sprache ausgerichtetes Modell zu erstellen. Für das Feintuning wird nur eine begrenzte Menge an Daten benötigt: Das Wissen, das das vortrainierte Modell erworben hat, wird "übertragen" (engl. transferred), daher der Begriff *Transfer Learning*.
+
+
+

+

+
+
+Das Feintuning eines Modells ist daher mit geringeren Zeit-, Daten-, Umwelt- und finanziellen Kosten verbunden. Es ist auch schneller und einfacher, verschiedene Modelle für das Feintuning auszuprobieren, da das Training mit geringeren Einschränkungen einhergeht als ein vollständiges Pretraining.
+
+Dieser Ansatz führt auch zu besseren Ergebnissen als ein Training von Grund auf (es sei denn, du hast viele Daten). Deshalb solltest du immer versuchen, ein vortrainiertes Modell zu nutzen - und zwar ein Modell, das so nah wie möglich an deiner Aufgabenstellung ist - und es für das Feintuning verwenden.
+
+## Grundlegende Architektur
+
+In diesem Abschnitt gehen wir auf die grundlegende Architektur des Transformer-Modells ein. Mach dir keine Sorgen, wenn du einige der Konzepte nicht verstehst. Im weiteren Verlauf folgen noch ausführliche Abschnitte zu den einzelnen Komponenten.
+
+
+
+## Einführung
+
+Das Modell besteht hauptsächlich aus zwei Blöcken:
+
+* **Encoder (links)**: Der Encoder, auch Kodierer genannt, empfängt einen Input und erstellt eine numerische Darstellung bzw. Repräsentation des Inputs (seiner Features, im Deutschen auch als Merkmale bezeichnet). Das bedeutet, dass das Modell darauf optimiert ist, ein Verständnis vom Input zu erlangen.
+* **Decoder (rechts)**: Der Decoder, auch bekannt als Dekodierer, verwendet die Repräsentation des Encoders (Features) zusammen mit anderen Inputs, um eine Zielsequenz zu generieren. Das bedeutet, dass das Modell darauf optimiert ist, einen Output zu generieren.
+
+
+

+

+
+
+Jede dieser Komponenten kann je nach Aufgabe unabhängig voneinander verwendet werden:
+
+* **Rein Encoder-basierte Modelle** ("Encoder-only Models"): Gut für Aufgaben, die ein Verständnis des Inputs erfordern, wie z. B. bei der Klassifizierung von Sätzen und der Eigennamenerkennung (NER).
+* **Rein Decoder-basierte Modelle** ("Decoder-only Models"): Gut geeignet für generative Aufgaben wie die Textgenerierung.
+* **Encoder-Decoder-basierte Modelle** bzw. **Sequence-to-Sequence-Modelle**: Gut für generative Aufgaben, die einen Input erfordern, wie z. B. Übersetzungen oder Zusammenfassungen.
+
+Wir werden diese Architekturen in späteren Abschnitten noch gesondert behandeln.
+
+## Attention-Layer
+
+Ein wesentliches Merkmal der Transformer-Modelle ist, dass sie mit speziellen Layern (im Deutschen auch als Schichten bezeichnet), den *Attention-Layern*, aufgebaut sind. Der Titel des Forschungsbeitrags, in dem die Transformer-Architektur vorgestellt wurde, lautete sogar ["Attention Is All You Need"](https://arxiv.org/abs/1706.03762)! Wir werden uns später im Kurs mit den Details von Attention-Layern befassen. Für den Moment musst du nur wissen, dass dieser Layer dem Modell sagt, dass es bei der Repräsentation eines jeden Worts in einem Satz, den du ihm übergeben hast, bestimmten Wörtern besondere Aufmerksamkeit schenken (und die anderen mehr oder weniger ignorieren) soll.
+
+Angenommen, du sollst einen Text aus dem Englischen ins Französische übersetzen. Bei dem Input "You like this course" muss ein Übersetzungsmodell auch das angrenzende Wort "You" berücksichtigen, um die richtige Übersetzung für das Wort "like" zu erhalten, denn im Französischen wird das Verb "like" je nach Subjekt unterschiedlich konjugiert. Der Rest des Satzes ist jedoch für die Übersetzung dieses Wortes nicht hilfreich. Genauso muss das Modell bei der Übersetzung von "this" auf das Wort "course" achten, denn "this" wird unterschiedlich übersetzt, je nachdem, ob das zugehörige Substantiv männlich oder weiblich ist. Auch hier spielen die anderen Wörter im Satz für die Übersetzung von "this" keine Rolle. Bei komplexeren Sätzen (und komplexeren Grammatikregeln) muss das Modell besonders auf Wörter achten, die weiter entfernt im Satz vorkommen, um jedes Wort richtig zu übersetzen.
+
+Das gleiche Konzept gilt für jede Aufgabenstellung, die mit natürlicher Sprache zu tun hat: Ein Wort an sich hat eine Bedeutung, aber diese Bedeutung hängt stark vom Kontext ab, der sich durch ein anderes Wort (oder Wörter) vor oder nach dem untersuchten Wort ergibt.
+
+Nachdem du nun eine Vorstellung davon hast, worum es bei Attention-Layern geht, nehmen wir die Transformer-Architektur genauer unter die Lupe.
+
+## Die ursprüngliche Architektur
+
+Die Transformer-Architektur wurde ursprünglich für die maschinelle Übersetzung entwickelt. Beim Training erhält der Encoder Inputs (Sätze) in einer bestimmten Sprache, während der Decoder die gleichen Sätze in der gewünschten Zielsprache erhält. Im Encoder können die Attention-Layer alle Wörter eines Satzes verwenden (denn wie wir gerade gesehen haben, kann die Übersetzung eines bestimmten Wortes sowohl von dem abhängen, was nach, als auch von dem, was vor dem Wort im Satz steht). Der Decoder arbeitet hingegen sequentiell und kann nur die Wörter im Satz berücksichtigen, die er bereits übersetzt hat (also nur die Wörter vor dem Wort, das gerade generiert wird). Wenn wir zum Beispiel die ersten drei Wörter der übersetzten Zielsequenz vorhergesagt haben, geben wir sie an den Decoder weiter, der dann alle Inputs des Encoders verwendet, um das vierte Wort vorherzusagen.
+
+Um das Training zu beschleunigen (insofern das Modell Zugriff auf die Zielsätze hat), wird der Decoder mit dem gesamten (vorherzusagenden) Zielsatz gefüttert, aber er darf keine nachfolgenden Wörter verwenden (wenn er Zugriff zum Wort an Position 2 hätte, während er versucht, das Wort an Position 2 vorherzusagen, wäre die Aufgabe nicht sonderlich schwer!). Wenn er zum Beispiel versucht, das vierte Wort vorherzusagen, hat der Attention-Layer nur Zugriff zu den Wörtern an den Positionen 1 bis 3.
+
+Die ursprüngliche Transformer-Architektur sah wie folgt aus - mit dem Encoder auf der linken und dem Decoder auf der rechten Seite:
+
+
+

+

+
+
+Beachte, dass die Attention des ersten Attention-Layers in einem Decoder-Block alle (vorangegangenen) Inputs, die der Decoder erhalten hat, berücksichtigt, während der zweite Attention-Layer den Output des Encoders verwendet. Im Rahmen der Vorhersage des aktuellen Wortes kann er also auf den gesamten Input-Satz zugreifen. Das ist vor allem deshalb nützlich, da es in den verschiedenen Sprachen unterschiedliche grammatikalische Regeln geben kann, wodurch die Wörter in einer anderen Reihenfolge aneinandergereiht werden. Ebenso könnte ein erst später im Satz enthaltener Zusammenhang dabei hilfreich sein, die beste Übersetzung eines bestimmten Wortes zu bestimmen.
+
+Die *Attention-Mask* kann auch im Encoder bzw. Decoder verwendet werden, um zu verhindern, dass das Modell bestimmte Wörter beachtet - zum Beispiel das spezielle Füllwort (engl. Padding Word), das verwendet wird, um alle Inputs auf die gleiche Länge zu bringen, wenn die Sätze zu Batches zusammengeführt werden.
+
+## Architekturen vs. Checkpoints
+
+Wenn wir uns in diesem Kurs mit Transformer-Modellen beschäftigen, wirst du auf *Architekturen*, *Checkpoints* und auch auf *Modelle* stoßen. Diese Begriffe haben alle eine etwas unterschiedliche Bedeutung:
+
+* **Architektur**: Dies ist das Skelett des Modells - die Definition jedes Layers und jeder Operation, die innerhalb des Modells stattfindet.
+* **Checkpoints**: Dies ist die Gewichtung, die für eine bestimmte Architektur geladen wird.
+* **Modell**: Dies ist ein Oberbegriff, der nicht so präzise ist wie "Architektur" oder "Checkpoint": Er kann beides bedeuten. In diesem Kurs wird jeweils explizit spezifiziert, ob es sich um eine *Architektur* oder um einen *Checkpoint* handelt, um Zweideutigkeiten zu vermeiden.
+
+BERT ist zum Beispiel eine Architektur, während `bert-base-cased` - ein Satz von Gewichten, der vom Google-Team für die erste Version von BERT trainiert wurde - ein Checkpoint ist. Man kann aber auch "das BERT-Modell" oder "das `bert-base-cased`-Modell" sagen.
diff --git a/chapters/de/chapter1/5.mdx b/chapters/de/chapter1/5.mdx
index 5f1b68f53..665dac512 100644
--- a/chapters/de/chapter1/5.mdx
+++ b/chapters/de/chapter1/5.mdx
@@ -1,22 +1,22 @@
-# Encoder-Modelle
-
-
-
-
-
-Encoder-Modelle verwenden nur den Encoder eines Transformer-Modells. Die Attention-Layer können zu jeder Zeit auf alle Wörter des Ausgangssatzes zugreifen. Diese Modelle werden häufig als Modelle mit "bidirektionaler" (engl. bi-directional) Attention bezeichnet und oft *Auto-Encoding-Modelle* genannt.
-
-Beim Pretraining dieser Modelle geht es in der Regel darum, einen bestimmten Satz auf irgendeine Weise zu verfälschen (z. B. indem zufällig Wörter darin maskiert werden) und das Modell dann damit zu betrauen, den ursprünglichen Satz zu finden bzw. zu rekonstruieren.
-
-Rein Encoder-basierte Modelle eignen sich am besten für Aufgaben, die ein Verständnis des gesamten Satzes erfordern, wie z. B. die Klassifizierung von Sätzen, die Eigennamenerkennung (bzw. allgemeiner die Klassifikation von Wörtern) und extraktive Frage-Antwort-Systeme.
-
-Zu dieser Modellfamilie gehören unter anderem:
-
-- [ALBERT](https://huggingface.co/transformers/model_doc/albert)
-- [BERT](https://huggingface.co/transformers/model_doc/bert)
-- [DistilBERT](https://huggingface.co/transformers/model_doc/distilbert)
-- [ELECTRA](https://huggingface.co/transformers/model_doc/electra)
-- [RoBERTa](https://huggingface.co/transformers/model_doc/roberta)
+# Encoder-Modelle
+
+
+
+
+
+Encoder-Modelle verwenden nur den Encoder eines Transformer-Modells. Die Attention-Layer können zu jeder Zeit auf alle Wörter des Ausgangssatzes zugreifen. Diese Modelle werden häufig als Modelle mit "bidirektionaler" (engl. bi-directional) Attention bezeichnet und oft *Auto-Encoding-Modelle* genannt.
+
+Beim Pretraining dieser Modelle geht es in der Regel darum, einen bestimmten Satz auf irgendeine Weise zu verfälschen (z. B. indem zufällig Wörter darin maskiert werden) und das Modell dann damit zu betrauen, den ursprünglichen Satz zu finden bzw. zu rekonstruieren.
+
+Rein Encoder-basierte Modelle eignen sich am besten für Aufgaben, die ein Verständnis des gesamten Satzes erfordern, wie z. B. die Klassifizierung von Sätzen, die Eigennamenerkennung (bzw. allgemeiner die Klassifikation von Wörtern) und extraktive Frage-Antwort-Systeme.
+
+Zu dieser Modellfamilie gehören unter anderem:
+
+- [ALBERT](https://huggingface.co/transformers/model_doc/albert)
+- [BERT](https://huggingface.co/transformers/model_doc/bert)
+- [DistilBERT](https://huggingface.co/transformers/model_doc/distilbert)
+- [ELECTRA](https://huggingface.co/transformers/model_doc/electra)
+- [RoBERTa](https://huggingface.co/transformers/model_doc/roberta)
diff --git a/chapters/de/chapter1/6.mdx b/chapters/de/chapter1/6.mdx
index 948c010a2..5d3725be0 100644
--- a/chapters/de/chapter1/6.mdx
+++ b/chapters/de/chapter1/6.mdx
@@ -1,21 +1,21 @@
-# Decoder-Modelle
-
-
-
-
-
-Decoder-Modelle verwenden nur den Decoder eines Transformer-Modells. Die Attention-Layer können bei jedem Schritt hinsichtlich eines bestimmten Wortes nur auf die Wörter zugreifen, die vor diesem Wort im Satz stehen. Diese Modelle werden oft als *autoregressive Modelle* bezeichnet.
-
-Beim Pretraining von Decoder-Modellen geht es in der Regel um die Vorhersage des nächsten Wortes im Satz.
-
-Diese Modelle sind am besten für Aufgaben geeignet, bei denen es um die Generierung von Texten geht.
-
-Zu dieser Modellfamilie gehören unter anderem:
-
-- [CTRL](https://huggingface.co/transformers/model_doc/ctrl)
-- [GPT](https://huggingface.co/docs/transformers/model_doc/openai-gpt)
-- [GPT-2](https://huggingface.co/transformers/model_doc/gpt2)
-- [Transformer XL](https://huggingface.co/transformers/model_doc/transformerxl)
+# Decoder-Modelle
+
+
+
+
+
+Decoder-Modelle verwenden nur den Decoder eines Transformer-Modells. Die Attention-Layer können bei jedem Schritt hinsichtlich eines bestimmten Wortes nur auf die Wörter zugreifen, die vor diesem Wort im Satz stehen. Diese Modelle werden oft als *autoregressive Modelle* bezeichnet.
+
+Beim Pretraining von Decoder-Modellen geht es in der Regel um die Vorhersage des nächsten Wortes im Satz.
+
+Diese Modelle sind am besten für Aufgaben geeignet, bei denen es um die Generierung von Texten geht.
+
+Zu dieser Modellfamilie gehören unter anderem:
+
+- [CTRL](https://huggingface.co/transformers/model_doc/ctrl)
+- [GPT](https://huggingface.co/docs/transformers/model_doc/openai-gpt)
+- [GPT-2](https://huggingface.co/transformers/model_doc/gpt2)
+- [Transformer XL](https://huggingface.co/transformers/model_doc/transformerxl)
diff --git a/chapters/de/chapter1/7.mdx b/chapters/de/chapter1/7.mdx
index 4bf04585f..1c28056f2 100644
--- a/chapters/de/chapter1/7.mdx
+++ b/chapters/de/chapter1/7.mdx
@@ -1,21 +1,21 @@
-# Sequence-to-Sequence-Modelle
-
-
-
-
-
-Encoder-Decoder-Modelle (auch *Sequence-to-Sequence-Modelle* genannt) verwenden beide Teile der Transformer-Architektur. Die Attention-Layer des Encoders können in jedem Schritt auf alle Wörter des Ausgangssatzes zugreifen, während die Attention-Layer des Decoders nur auf die Wörter zugreifen können, die vor einem bestimmten Wort des Inputs stehen.
-
-Das Pretraining dieser Modelle kann wie das Pretraining von rein Encoder- oder Decoder-basierten Modellen erfolgen, ist aber in der Regel etwas komplexer. Beim Pretraining von [T5](https://huggingface.co/t5-base) werden zum Beispiel zufällige Textabschnitte (die mehrere Wörter enthalten können) durch ein einzelnes spezielles Maskierungswort ersetzt, und das Ziel (engl. Pretraining Objective) besteht dann darin, den Text vorherzusagen, der durch dieses Maskierungswort ersetzt bzw. verdeckt wurde.
-
-Sequence-to-Sequence-Modelle eignen sich am besten für Aufgaben, bei denen es darum geht, neue Sätze in Abhängigkeit von einem bestimmten Input zu generieren, z. B. bei der Zusammenfassung, Übersetzung oder generativen Frage-Antwort-Systemen.
-
-Vertreter dieser Modellfamilie sind u. a.:
-
-- [BART](https://huggingface.co/transformers/model_doc/bart)
-- [mBART](https://huggingface.co/transformers/model_doc/mbart)
-- [Marian](https://huggingface.co/transformers/model_doc/marian)
-- [T5](https://huggingface.co/transformers/model_doc/t5)
+# Sequence-to-Sequence-Modelle
+
+
+
+
+
+Encoder-Decoder-Modelle (auch *Sequence-to-Sequence-Modelle* genannt) verwenden beide Teile der Transformer-Architektur. Die Attention-Layer des Encoders können in jedem Schritt auf alle Wörter des Ausgangssatzes zugreifen, während die Attention-Layer des Decoders nur auf die Wörter zugreifen können, die vor einem bestimmten Wort des Inputs stehen.
+
+Das Pretraining dieser Modelle kann wie das Pretraining von rein Encoder- oder Decoder-basierten Modellen erfolgen, ist aber in der Regel etwas komplexer. Beim Pretraining von [T5](https://huggingface.co/t5-base) werden zum Beispiel zufällige Textabschnitte (die mehrere Wörter enthalten können) durch ein einzelnes spezielles Maskierungswort ersetzt, und das Ziel (engl. Pretraining Objective) besteht dann darin, den Text vorherzusagen, der durch dieses Maskierungswort ersetzt bzw. verdeckt wurde.
+
+Sequence-to-Sequence-Modelle eignen sich am besten für Aufgaben, bei denen es darum geht, neue Sätze in Abhängigkeit von einem bestimmten Input zu generieren, z. B. bei der Zusammenfassung, Übersetzung oder generativen Frage-Antwort-Systemen.
+
+Vertreter dieser Modellfamilie sind u. a.:
+
+- [BART](https://huggingface.co/transformers/model_doc/bart)
+- [mBART](https://huggingface.co/transformers/model_doc/mbart)
+- [Marian](https://huggingface.co/transformers/model_doc/marian)
+- [T5](https://huggingface.co/transformers/model_doc/t5)
diff --git a/chapters/de/chapter1/8.mdx b/chapters/de/chapter1/8.mdx
index 5e15e56e9..be928fdba 100644
--- a/chapters/de/chapter1/8.mdx
+++ b/chapters/de/chapter1/8.mdx
@@ -1,32 +1,32 @@
-# Bias und Einschränkungen
-
-
-
-Wenn du vorhast, ein vortrainiertes Modell oder eine feingetunte Modellversion in der Produktion zu verwenden, sei dir bitte darüber im Klaren, dass diese zwar leistungsstarke Werkzeuge sind, allerdings aber auch ihre Grenzen haben. Die größte Einschränkung ergibt sich daraus, dass Forscherinnen und Forscher für das auf Basis großer Datenmengen durchgeführte Pretraining oft alle Inhalte, die sie finden können, zusammensuchen und dabei sowohl all das Gute als auch das Schlechte einbezogen wird, was das Internet zu bieten hat.
-
-Greifen wir zur Veranschaulichung noch einmal das Beispiel einer `fill-mask`-Pipeline mit dem BERT-Modell auf:
-
-```python
-from transformers import pipeline
-
-unmasker = pipeline("fill-mask", model="bert-base-uncased")
-result = unmasker("This man works as a [MASK].")
-print([r["token_str"] for r in result])
-
-result = unmasker("This woman works as a [MASK].")
-print([r["token_str"] for r in result])
-```
-
-```python out
-['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic']
-['nurse', 'waitress', 'teacher', 'maid', 'prostitute']
-```
-
-Wenn das Modell aufgefordert wird, das fehlende Wort in diesen beiden Sätzen zu ergänzen, gibt es lediglich eine geschlechtsneutrale Antwort (Kellnerin/Kellner - waitress/waiter). Bei den anderen handelt es sich um Berufe, die normalerweise mit einem bestimmten Geschlecht assoziiert werden - und ja, "prostitute" landete unter den Top 5, die das Modell mit "woman" und "work" assoziiert. Und das, obwohl BERT eines der wenigen Transformer-Modelle ist, das nicht auf Daten aus dem gesamten Internet beruht, sondern auf vermeintlich neutralen Daten (es wurde auf dem [englischsprachigen Wikipedia-](https://huggingface.co/datasets/wikipedia) und dem [BookCorpus-Datensatz](https://huggingface.co/datasets/bookcorpus) trainiert).
-
-Wenn du diese Werkzeuge verwendest, musst du daher im Hinterkopf behalten, dass das ursprüngliche Modell, das du verwendest, sehr leicht sexistische, rassistische oder homophobe Inhalte hervorbringen könnte. Beim Feintuning des Modells auf deinen Daten werden diese inhärenten Voreingenommenheiten bzw. Vorurteile (engl. Bias) nicht verschwinden.
+# Bias und Einschränkungen
+
+
+
+Wenn du vorhast, ein vortrainiertes Modell oder eine feingetunte Modellversion in der Produktion zu verwenden, sei dir bitte darüber im Klaren, dass diese zwar leistungsstarke Werkzeuge sind, allerdings aber auch ihre Grenzen haben. Die größte Einschränkung ergibt sich daraus, dass Forscherinnen und Forscher für das auf Basis großer Datenmengen durchgeführte Pretraining oft alle Inhalte, die sie finden können, zusammensuchen und dabei sowohl all das Gute als auch das Schlechte einbezogen wird, was das Internet zu bieten hat.
+
+Greifen wir zur Veranschaulichung noch einmal das Beispiel einer `fill-mask`-Pipeline mit dem BERT-Modell auf:
+
+```python
+from transformers import pipeline
+
+unmasker = pipeline("fill-mask", model="bert-base-uncased")
+result = unmasker("This man works as a [MASK].")
+print([r["token_str"] for r in result])
+
+result = unmasker("This woman works as a [MASK].")
+print([r["token_str"] for r in result])
+```
+
+```python out
+['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic']
+['nurse', 'waitress', 'teacher', 'maid', 'prostitute']
+```
+
+Wenn das Modell aufgefordert wird, das fehlende Wort in diesen beiden Sätzen zu ergänzen, gibt es lediglich eine geschlechtsneutrale Antwort (Kellnerin/Kellner - waitress/waiter). Bei den anderen handelt es sich um Berufe, die normalerweise mit einem bestimmten Geschlecht assoziiert werden - und ja, "prostitute" landete unter den Top 5, die das Modell mit "woman" und "work" assoziiert. Und das, obwohl BERT eines der wenigen Transformer-Modelle ist, das nicht auf Daten aus dem gesamten Internet beruht, sondern auf vermeintlich neutralen Daten (es wurde auf dem [englischsprachigen Wikipedia-](https://huggingface.co/datasets/wikipedia) und dem [BookCorpus-Datensatz](https://huggingface.co/datasets/bookcorpus) trainiert).
+
+Wenn du diese Werkzeuge verwendest, musst du daher im Hinterkopf behalten, dass das ursprüngliche Modell, das du verwendest, sehr leicht sexistische, rassistische oder homophobe Inhalte hervorbringen könnte. Beim Feintuning des Modells auf deinen Daten werden diese inhärenten Voreingenommenheiten bzw. Vorurteile (engl. Bias) nicht verschwinden.
diff --git a/chapters/de/chapter1/9.mdx b/chapters/de/chapter1/9.mdx
index f65fc8fa4..843d59a5e 100644
--- a/chapters/de/chapter1/9.mdx
+++ b/chapters/de/chapter1/9.mdx
@@ -1,16 +1,16 @@
-# Zusammenfassung
-
-
-
-In diesem Kapitel hast du gelernt, wie du verschiedene CL-Aufgaben mit der High-Level-Funktion `pipeline()` aus der 🤗 Transformers-Bibliothek angehen kannst. Du hast auch erfahren, wie du im Hub nach Modellen suchen und sie nutzen kannst, und wie du die Inference API verwenden kannst, um die Modelle direkt in deinem Browser zu testen.
-
-Wir haben besprochen, wie Transformer-Modelle im Großen und Ganzen funktionieren, und haben die Bedeutung von Tranfer Learning und Feintuning erläutert. Ein wichtiger Aspekt ist, dass du entweder die gesamte Architektur, nur den Encoder oder auch nur den Decoder verwenden kannst - je nachdem, welche Art von Aufgabe du lösen willst. Die nachfolgende Tabelle gibt noch einmal einen guten Überblick:
-
-| Modell | Beispiele | Aufgaben (Tasks) |
-|-----------------|--------------------------------------------|----------------------------------------------------------------------------------|
-| Encoder | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa | Klassifizierung von Sätzen, Eigennamenerkennung/NER, Extraktive Frage-Antwort-Systeme |
-| Decoder | CTRL, GPT, GPT-2, Transformer XL | Textgenerierung |
-| Encoder-Decoder | BART, T5, Marian, mBART | Automatische Textzusammenfassung, Maschinelle Übersetzung, Generative Frage-Antwort-Systeme |
+# Zusammenfassung
+
+
+
+In diesem Kapitel hast du gelernt, wie du verschiedene CL-Aufgaben mit der High-Level-Funktion `pipeline()` aus der 🤗 Transformers-Bibliothek angehen kannst. Du hast auch erfahren, wie du im Hub nach Modellen suchen und sie nutzen kannst, und wie du die Inference API verwenden kannst, um die Modelle direkt in deinem Browser zu testen.
+
+Wir haben besprochen, wie Transformer-Modelle im Großen und Ganzen funktionieren, und haben die Bedeutung von Tranfer Learning und Feintuning erläutert. Ein wichtiger Aspekt ist, dass du entweder die gesamte Architektur, nur den Encoder oder auch nur den Decoder verwenden kannst - je nachdem, welche Art von Aufgabe du lösen willst. Die nachfolgende Tabelle gibt noch einmal einen guten Überblick:
+
+| Modell | Beispiele | Aufgaben (Tasks) |
+|-----------------|--------------------------------------------|----------------------------------------------------------------------------------|
+| Encoder | ALBERT, BERT, DistilBERT, ELECTRA, RoBERTa | Klassifizierung von Sätzen, Eigennamenerkennung/NER, Extraktive Frage-Antwort-Systeme |
+| Decoder | CTRL, GPT, GPT-2, Transformer XL | Textgenerierung |
+| Encoder-Decoder | BART, T5, Marian, mBART | Automatische Textzusammenfassung, Maschinelle Übersetzung, Generative Frage-Antwort-Systeme |
diff --git a/chapters/de/chapter3/1.mdx b/chapters/de/chapter3/1.mdx
index 7d275b917..7f1611828 100644
--- a/chapters/de/chapter3/1.mdx
+++ b/chapters/de/chapter3/1.mdx
@@ -1,26 +1,26 @@
-
-
-# Einführung
-
-
-
-In [Kapitel 2](/course/chapter2) haben wir behandelt, wie man Tokenizer und vortrainierte Modelle verwendet, um Vorhersagen zu treffen. Was passiert aber, wenn wir ein vortrainiertes Modell für unseren eigenen Datensatz optimieren möchten? Das ist das Thema dieses Kapitels! Folgendes wirst du lernen:
-
-{#if fw === 'pt'}
-* Wie bereitet man einen großen Datensatz aus dem Hub vor?
-* Wie nutzt man die höhere `Trainer` API um Modelle zu fein-tunen?
-* Wie implementiert man eine benutzerdefinierte Trainingsschleife
-* Wie nutzen wir die 🤗 Accelerate Bibliothek für benutzerdefinierte Trainingschleifen auf verteilten Systemen
-
-{:else}
-* Wie bereitet man einen großen Datensatz aus dem Hub vor?
-* Wie nutzt man Keras um Modelle zu fein-tunen?
-* Wie setzt man Keras für Vorhersagen ein?
-* Wie implementiert benutzerdefinierte Metriken?
-
-{/if}
-
+
+
+# Einführung
+
+
+
+In [Kapitel 2](/course/chapter2) haben wir behandelt, wie man Tokenizer und vortrainierte Modelle verwendet, um Vorhersagen zu treffen. Was passiert aber, wenn wir ein vortrainiertes Modell für unseren eigenen Datensatz optimieren möchten? Das ist das Thema dieses Kapitels! Folgendes wirst du lernen:
+
+{#if fw === 'pt'}
+* Wie bereitet man einen großen Datensatz aus dem Hub vor?
+* Wie nutzt man die höhere `Trainer` API um Modelle zu fein-tunen?
+* Wie implementiert man eine benutzerdefinierte Trainingsschleife
+* Wie nutzen wir die 🤗 Accelerate Bibliothek für benutzerdefinierte Trainingschleifen auf verteilten Systemen
+
+{:else}
+* Wie bereitet man einen großen Datensatz aus dem Hub vor?
+* Wie nutzt man Keras um Modelle zu fein-tunen?
+* Wie setzt man Keras für Vorhersagen ein?
+* Wie implementiert benutzerdefinierte Metriken?
+
+{/if}
+
Um deine trainierten Checkpoints auf den Hugging Face Hub hochzuladen, benötigst du ein huggingface.co-Konto: [Erstelle ein Konto](https://huggingface.co/join)
\ No newline at end of file
diff --git a/chapters/de/chapter3/2.mdx b/chapters/de/chapter3/2.mdx
index 1c9aaad0a..03e12b3c9 100644
--- a/chapters/de/chapter3/2.mdx
+++ b/chapters/de/chapter3/2.mdx
@@ -1,376 +1,376 @@
-
-
-# Vorbereitung der Daten
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-{#if fw === 'pt'}
-Wir fahren mit dem Beispiel aus dem [vorigen Kapitel](/course/chapter2) fort. Folgenderweise würden wir einen Sequenzklassifikator mit einem Batch in PyTorch trainieren:
-
-```python
-import torch
-from torch.optim import AdamW
-from transformers import AutoTokenizer, AutoModelForSequenceClassification
-
-# Genau wie vorher
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-sequences = [
- "I've been waiting for a HuggingFace course my whole life.", # Ich habe mein ganzes Leben auf einen HuggingFace-Kurs gewartet.
- "This course is amazing!", # Dieser Kurs ist fantastisch!
-]
-batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
-
-# Dies ist neu
-batch["labels"] = torch.tensor([1, 1])
-
-optimizer = AdamW(model.parameters())
-loss = model(**batch).loss
-loss.backward()
-optimizer.step()
-```
-{:else}
-Wir fahren mit dem Beispiel aus dem [vorigen Kapitel](/course/chapter2) fort. Folgenderweise würden wir einen Sequenzklassifikator mit einem Batch in Tensorflow trainieren:
-
-```python
-import tensorflow as tf
-import numpy as np
-from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
-
-# Genau wie vorher
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
-sequences = [
- "I've been waiting for a HuggingFace course my whole life.", # Ich habe mein ganzes Leben auf einen HuggingFace-Kurs gewartet.
- "This course is amazing!", # Dieser Kurs ist fantastisch!
-]
-batch = dict(tokenizer(sequences, padding=True, truncation=True, return_tensors="tf"))
-
-# Dies ist neu
-model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
-labels = tf.convert_to_tensor([1, 1])
-model.train_on_batch(batch, labels)
-```
-{/if}
-
-Natürlich würde das Training von Modellen mit nur zwei Sätzen keine sonderlich guten Ergebnisse liefern. Um bessere Ergebnisse zu erzielen, müssen wir einen größeren Datensatz vorbereiten.
-
-In diesem Abschnitt verwenden wir den MRPC-Datensatz (Microsoft Research Paraphrase Corpus) als Beispiel. Dieser wurde in einem [Paper](https://www.aclweb.org/anthology/I05-5002.pdf) von William B. Dolan und Chris Brockett veröffentlicht. Der Datensatz besteht aus insgesamt 5.801 Satzpaaren und enthält ein Label, das angibt, ob es sich bei einem Paar um Paraphrasen handelt (d.h. ob beide Sätze dasselbe bedeuten). Wir haben diesen Datensatz für dieses Kapitel ausgewählt, weil es sich um einen kleinen Datensatz handelt, sodass es einfach ist, während dem Training zu experimentieren.
-
-### Laden eines Datensatzes vom Hub
-
-{#if fw === 'pt'}
-
-{:else}
-
-{/if}
-
-Das Hub enthält nicht nur Modelle; Es hat auch mehrere Datensätze in vielen verschiedenen Sprachen. Du kannst die Datensätze [hier](https://huggingface.co/datasets) durchsuchen, und wir empfehlen, einen weiteren Datensatz zu laden und zu verarbeiten, sobald Sie diesen Abschnitt abgeschlossen haben (die Dokumentation befindet sich [hier](https://huggingface.co/docs/datasets/loading)). Aber jetzt konzentrieren wir uns auf den MRPC-Datensatz! Dies ist einer der 10 Datensätze, aus denen sich das [GLUE-Benchmark](https://gluebenchmark.com/) zusammensetzt. Dies ist ein akademisches Benchmark, das verwendet wird, um die Performance von ML-Modellen in 10 verschiedenen Textklassifizierungsaufgaben zu messen.
-
-Die Bibliothek 🤗 Datasets bietet einen leichten Befehl zum Herunterladen und Caching eines Datensatzes aus dem Hub. Wir können den MRPC-Datensatz wie folgt herunterladen:
-
-⚠️ ** Warnung** Stelle sicher, dass `datasets` installiert ist, indem du `pip install datasets` ausführst. Dann lade den MRPC-Datensatz und drucke ihn aus, um zu sehen, was er enthält.
-
-
-```py
-from datasets import load_dataset
-
-raw_datasets = load_dataset("glue", "mrpc")
-raw_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['sentence1', 'sentence2', 'label', 'idx'],
- num_rows: 3668
- })
- validation: Dataset({
- features: ['sentence1', 'sentence2', 'label', 'idx'],
- num_rows: 408
- })
- test: Dataset({
- features: ['sentence1', 'sentence2', 'label', 'idx'],
- num_rows: 1725
- })
-})
-```
-
-Wie du sehen kannst, erhalten wir ein `DatasetDict`-Objekt, das die Trainingsdaten, die Validierungsdaten und die Testdaten enthält. Jedes Objekt enthält mehrere Spalten (`sentence1`, `sentence2`, `label` und `idx`) und eine unterschiedliche Anzahl an Zeilen, dies ist die Anzahl der Elemente in jedem Datensatz (also gibt es 3.668 Satzpaare in den Trainingsdaten, 408 in den Validierungsdaten und 1.725 in den Testdaten).
-
-Dieser Befehl lädt das Dataset herunter und speichert es im Cache, standardmäßig in *~/.cache/huggingface/dataset*. Wir Erinnern uns an Kapitel 2, dass der Cache-Ordner anpasst werden kann, indem man die Umgebungsvariable `HF_HOME` setzt.
-
-Wir können auf jedes Satzpaar in unserem `raw_datasets`-Objekt zugreifen, indem wir wie bei einem Dictionary einen Schlüsselwert als Index verwenden:
-
-```py
-raw_train_dataset = raw_datasets["train"]
-raw_train_dataset[0]
-```
-
-```python out
-{'idx': 0,
- 'label': 1,
- 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
- 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
-```
-
-Wir stellen fest, dass die Labels bereits Ganzzahlen sind, sodass wir dort keine Vorverarbeitung durchführen müssen. Wir können die `features` von `raw_train_dataset` untersuchen, um zu erfahren, welche Ganzzahl welchem Label entspricht. Der folgende Befehl gibt uns den Variablentyp zurück:
-
-```py
-raw_train_dataset.features
-```
-
-```python out
-{'sentence1': Value(dtype='string', id=None),
- 'sentence2': Value(dtype='string', id=None),
- 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
- 'idx': Value(dtype='int32', id=None)}
-```
-
-Hinter den Kulissen ist `label` vom Typ `ClassLabel`, und die Zuordnung von Ganzzahlen zum Labelnamen wird im Ordner *names* gespeichert. `0` entspricht `not_equivalent`, also "nicht äquivalent", und `1` entspricht `equivalent`, also "äquivalent".
-
-> [!TIP]
-> ✏️ **Probier es aus!** Sieh dir das Element 15 der Trainingsdaten und Element 87 des Validierungsdaten an. Was sind ihre Labels?
-
-### Vorverarbeitung eines Datensatzes
-
-{#if fw === 'pt'}
-
-{:else}
-
-{/if}
-
-Um den Datensatz vorzubereiten, müssen wir den Text in Zahlen umwandeln, die das Modell sinnvoll verarbeiten kann. Im [vorherigen Kapitel](/course/chapter2) haben wir gesehen, dass dies mit einem Tokenizer gemacht wird. Wir können den Tokenizer mit einem Satz oder einer Liste von Sätzen füttern, sodass wir die ersten und zweiten Sätze jedes Paares wie folgt direkt tokenisieren können:
-
-```py
-from transformers import AutoTokenizer
-
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
-tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
-```
-
-Wir können jedoch nicht einfach zwei Sequenzen an das Modell übergeben und eine Vorhersage erhalten, ob die beiden Sätze paraphrasiert sind oder nicht. Wir müssen die beiden Sequenzen als Paar behandeln und die entsprechende Vorverarbeitung anwenden. Glücklicherweise kann der Tokenizer auch ein Sequenzpaar nehmen und es so vorbereiten, wie es unser BERT-Modell erwartet:
-
-```py
-inputs = tokenizer("This is the first sentence.", "This is the second one.")
-inputs
-```
-
-```python out
-{
- 'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
- 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
- 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
-}
-```
-
-In [Kapitel 2](/course/chapter2) haben wir die Schlüsselwerte `input_ids` und `attention_mask` behandelt, allerdings haben wir es aufgeschoben, über `token_type_ids` zu sprechen. In diesem Beispiel teilt diese dem Modell mit, welcher Teil des Input der erste Satz und welcher der zweite Satz ist.
-
-> [!TIP]
-> ✏️ **Probier es aus!** Nimm Element 15 der Trainingsdaten und tokenisiere die beiden Sätze separat und als Paar. Wo liegt der Unterschied zwischen den beiden Ergebnissen?
-
-Wenn wir die IDs in `input_ids` zurück in Worte dekodieren:
-
-```py
-tokenizer.convert_ids_to_tokens(inputs["input_ids"])
-```
-
-dann bekommen wir:
-
-```python out
-['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
-```
-
-Wir sehen also wenn es zwei Sätze gibt, dass das Modell erwartet, dass die Inputs die Form "[CLS] Satz1 [SEP] Satz2 [SEP]" haben. Wenn wir dies mit den `token_type_ids` abgleichen, erhalten wir:
-
-```python out
-['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
-[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
-```
-
-Wie du sehen kannst, haben die Teile der Eingabe, die `[CLS] Satz1 [SEP]` entsprechen, alle eine Token-Typ-ID von `0`, während die anderen Teile, die `Satz2 [SEP]` entsprechen, alle einer Token-Typ-ID von `1` enthalten.
-
-Beachte, dass die Auswahl eines anderen Checkpoints nicht unbedingt die `token_type_ids` in Ihren tokenisierten Inputs haben (z.B. werden sie nicht zurückgegeben, wenn ein DistilBERT-Modell verwendet wird). Sie werden nur zurückgegeben, wenn das Modell weiß was damit zu tun ist, weil es die Toke-Typ-Ids während des Vortrainings gesehen hat.
-
-In diesem Fall ist BERT mit Token-Typ-IDs vortrainiert worden, und zusätzlich zu dem maskierten Sprachmodellierungsziel aud [Kapitel 1](/course/chapter1), hat es ein zusätzliches Vorhersageziel namens _next sentence prediction_ (d.h. Vorhersage des nächsten Satzes). Das Ziel dieser Aufgabe ist es, die Beziehung zwischen Satzpaaren zu modellieren.
-
-Bei der Vorhersage des nächsten Satzes werden dem Modell Satzpaare (mit zufällig maskierten Token) bereitgestellt und erwartet, vorherzusagen, ob auf den ersten Satz der zweite Satz folgt. Um die Aufgabe non-trivial zu machen, folgen sich die Hälfte der Sätze in dem Originaldokument, aus dem sie extrahiert wurden, aufeinander, und in der anderen Hälfte stammen die beiden Sätze aus zwei verschiedenen Dokumenten.
-
-Im Allgemeinen muss man sich keine Gedanken darüber machen, ob Ihre tokenisierten Inputs `token_type_ids` enthalten oder nicht: Solange du denselben Checkpoint für den Tokenizer und das Modell verwendest, ist alles in Ordnung, da der Tokenizer weiß, was er dem Modell bereitstellen soll.
-
-Nachdem wir nun gesehen haben, wie unser Tokenizer mit einem Satzpaar umgehen kann, können wir damit unseren gesamten Datensatz tokenisieren: Wie im [vorherigen Kapitel](/course/chapter2) können wir dem Tokenizer eine Liste von Satzpaaren einspeisen, indem du ihm die Liste der ersten Sätze und dann die Liste der zweiten Sätze gibst. Dies ist auch kompatibel mit den Optionen zum Padding und Trunkieren, die wir in [Kapitel 2](/course/chapter2) gesehen haben. Eine Möglichkeit, den Trainingsdatensatz vorzuverarbeiten, ist also:
-
-```py
-tokenized_dataset = tokenizer(
- raw_datasets["train"]["sentence1"],
- raw_datasets["train"]["sentence2"],
- padding=True,
- truncation=True,
-)
-```
-
-Das funktioniert gut, hat aber den Nachteil, dass ein Dictionary zurückgegeben wird (mit unseren Schlüsselwörtern `input_ids`, `attention_mask` und `token_type_ids` und Werten aus Listen von Listen). Es funktioniert auch nur, wenn du genügend RAM hast, um den gesamten Datensatz während der Tokenisierung zu im RAM zwischen zu speichern (während die Datensätze aus der Bibliothek 🤗 Datasets [Apache Arrow](https://arrow.apache.org/) Dateien sind, die auf der Festplatte gespeichert sind, sodass nur die gewünschten Samples im RAM geladen sind).
-
-Um die Daten als Datensatz zu speichern, verwenden wir die Methode [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map). Dies gewährt uns zusätzliche Flexibilität, wenn wir zusätzliche Vorverarbeitung als nur die Tokenisierung benötigen. Die `map()`-Methode funktioniert, indem sie eine Funktion auf jedes Element des Datensatzes anwendet, also definieren wir eine Funktion, die unsere Inputs tokenisiert:
-
-```py
-def tokenize_function(example):
- return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
-```
-
-Diese Funktion nimmt ein Dictionary (wie die Elemente unseres Datensatzes) und gibt ein neues Dictionary mit den Schlüsselwerten `input_ids`, `attention_mask` und `token_type_ids` zurück. Beachte, dass es auch funktioniert, wenn das `example`-Dictionary mehrere Beispiele enthält (jeder Schlüsselwert als Liste von Sätzen), da der `Tokenizer`, wie zuvor gesehen, mit Listen von Satzpaaren arbeitet. Dadurch können wir die Option `batched=True` in unserem Aufruf von `map()` verwenden, was die Tokenisierung erheblich beschleunigt. Der `tokenizer` wurde in Rust geschriebenen und ist in der Bibliothek [🤗 Tokenizers](https://github.com/huggingface/tokenizers) verfügbar. Dieser Tokenizer kann sehr schnell arbeiten, wenn wir ihm viele Inputs auf einmal zum Verarbeiten geben. Note that we've left the `padding` argument out in our tokenization function for now.
-
-Beachte, dass wir das `padding`-Argument vorerst in unserer Tokenisierungsfunktion ausgelassen haben. Dies liegt daran, dass das Anwenden von Padding auf alle Elemente unserer Daten auf die maximale Länge nicht effizient ist: Es ist besser, die Proben aufzufüllen, wenn wir ein Batch erstellen, da wir dann nur auf die maximale Länge in diesem Batch auffüllen müssen und nicht auf die maximale Länge in den gesamten Datensatz. Dies kann viel Zeit und Rechenleistung sparen, besonders wenn die Eingaben stark variable Längen haben!
-
-So wenden wir die Tokenisierungsfunktion auf alle unsere Datensätze gleichzeitig an. In unserem Aufruf von `map` verwenden wir `batched=True`, damit die Funktion auf mehrere Elemente des Datensatzes gleichzeitig angewendet wird und nicht auf jedes Element separat. Dies ermöglicht eine schnellere Vorverarbeitung.
-
-```py
-tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
-tokenized_datasets
-```
-
-Die Bibliothek 🤗 Datasets verarbeitet Datensätzen indem sie neue Felder hinzuzufügen, eines für jeden Schlüssel im Dictionary, der von der Vorverarbeitungsfunktion zurückgegeben wird:
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
- num_rows: 3668
- })
- validation: Dataset({
- features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
- num_rows: 408
- })
- test: Dataset({
- features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
- num_rows: 1725
- })
-})
-```
-
-Du kannst sogar Multiprocessing verwenden, wenn du die Vorverarbeitungsfunktion mit `map()` anwendest, indem du ein `num_proc`-Argument übergiebst. Wir haben dies hier nicht getan, weil die 🤗 Tokenizers-Bibliothek bereits mehrere Threads verwendet, um unsere Samples schneller zu tokenisieren. Wenn du keinen schnellen Tokenizer verwendest, der von dieser Bibliothek unterstützt wird, würde dies allerdings die Vorverarbeitung beschleunigen.
-
-Unsere `tokenize_function` gibt ein Dictionary mit den Schlüsselwerten `input_ids`, `attention_mask` und `token_type_ids` zurück, also werden diese drei Felder zu allen Splits unseres Datensatzes hinzugefügt. Beachte, dass wir auch vorhandene Felder ändern könnten, wenn unsere Vorverarbeitungsfunktion einen neuen Wert für einen vorhandenen Schlüsselwert in dem Datensatz zurückgegeben hätte, auf den wir `map()` angewendet haben.
-
-Zuletzt, müssen wir alle Beispiele auf die Länge des längsten Elements aufzufüllen, wenn wir Elemente zusammenfassen – eine Technik, die wir als *Dynamisches Padding* bezeichnen.
-
-### Dynamisches Padding
-
-
-
-{#if fw === 'pt'}
-
-Die Funktion, die für das Zusammenstellen von Samples innerhalb eines Batches verantwortlich ist, wird als *Collate-Funktion* bezeichnet. Es ist ein Argument, das du übergeben kannst, wenn du einen `DataLoader` baust, wobei es standardmäßig eine Funktion ist, die die Daten in PyTorch-Tensoren umwandelt und zusammenfügt (rekursiv wenn die Elemente Listen, Tupel oder Dictionaries sind). Dies ist in unserem Fall nicht möglich, da die Inputs nicht alle gleich groß sind. Das Padding haben wir bewusst aufgeschoben, um es bei jedem Batch nur bei Bedarf anzuwenden und überlange Inputs mit massivem Padding zu vermeiden. Dies beschleunigt das Training zwar, aber beachte, dass das Training auf einer TPU Probleme verursachen kann – TPUs bevorzugen feste Formen, auch wenn das ein zusätzliches Padding erfordert.
-
-{:else}
-
-Die Funktion, die für das Zusammenstellen von Samples innerhalb eines Batches verantwortlich ist, wird als *Collate-Funktion* bezeichnet. Es ist ein Argument, das du übergeben kannst, wenn du einen `DataLoader` baust, wobei es standardmäßig eine Funktion ist, die die Daten in tf.Tensor umwandelt und zusammenfügt (rekursiv wenn die Elemente Listen, Tupel oder Dictionaries sind). Dies ist in unserem Fall nicht möglich, da die Inputs nicht alle gleich groß sind. Das Padding haben wir bewusst aufgeschoben, um es bei jedem Batch nur bei Bedarf anzuwenden und überlange Inputs mit massivem Padding zu vermeiden. Dies beschleunigt das Training zwar, aber beachte, dass das Training auf einer TPU Probleme verursachen kann – TPUs bevorzugen feste Formen, auch wenn das ein zusätzliches Padding erfordert.
-
-{/if}
-
-In der Praxis müssen wir eine Collate-Funktion definieren, die die korrekte Menge an Padding auf die Elemente des Datensatzes anwendet, die wir in einem Batch haben möchten. Glücklicherweise stellt uns die 🤗 Transformers-Bibliothek über `DataCollatorWithPadding` eine solche Funktion zur Verfügung. Wenn sie instanziert wird, braucht es einen Tokenizer (um zu wissen, welches Padding-token verwendet werden soll und ob das Modell erwartet, dass sich das Padding links oder rechts von den Inputs befindet) und übernimmt alles was wir brauchen:
-
-{#if fw === 'pt'}
-```py
-from transformers import DataCollatorWithPadding
-
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
-```
-{:else}
-```py
-from transformers import DataCollatorWithPadding
-
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
-```
-{/if}
-
-Um dieses neue Werkzeug zu testen, nehmen wir einige Elemente aus den Trainingsdaten, die wir als Batch verwenden möchten. Hier entfernen wir die Spalten `idx`, `sentence1` und `sentence2`, da sie nicht benötigt werden und Strings enthalten (wir können keine Tensoren mit Strings erstellen) und sehen uns die Länge jedes Eintrags im Batch an:
-
-```py
-samples = tokenized_datasets["train"][:8]
-samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
-[len(x) for x in samples["input_ids"]]
-```
-
-```python out
-[50, 59, 47, 67, 59, 50, 62, 32]
-```
-
-Wenig überraschen erhalten wir Samples unterschiedlicher Länge von 32 bis 67. Dynamisches Padding bedeutet, dass die Elemente in diesem Batch alle auf eine Länge von 67 aufgefüllt werden, die maximale Länge innerhalb des Batches. Ohne dynamisches Auffüllen müssten alle Einträge auf die maximale Länge im gesamten Datensatz oder auf die maximale Länge die das Modell akzeptiert, aufgefüllt werden. Lass uns noch einmal überprüfen, ob unser `data_collator` den Stapel dynamisch richtig auffüllt:
-
-```py
-batch = data_collator(samples)
-{k: v.shape for k, v in batch.items()}
-```
-
-{#if fw === 'tf'}
-
-```python out
-{'attention_mask': TensorShape([8, 67]),
- 'input_ids': TensorShape([8, 67]),
- 'token_type_ids': TensorShape([8, 67]),
- 'labels': TensorShape([8])}
-```
-
-{:else}
-
-```python out
-{'attention_mask': torch.Size([8, 67]),
- 'input_ids': torch.Size([8, 67]),
- 'token_type_ids': torch.Size([8, 67]),
- 'labels': torch.Size([8])}
-```
-
-Das sieht gut aus! Jetzt, da wir vom Rohtext zu Batches übergegangen sind, mit denen unser Modell umgehen kann, sind wir bereit zum fein-tunen!
-
-{/if}
-
-> [!TIP]
-> ✏️ **Probier es aus!** Repliziere die Vorverarbeitung auf dem GLUE SST-2-Datensatz. Es ist ein bisschen anders, da es aus einzelnen Sätzen statt aus Paaren besteht, aber der Rest von dem, was wir gemacht haben, sollte gleich aussehen. Alternative wäre eine schwierigere Herausforderung, eine Vorverarbeitungsfunktion zu schreiben, die bei allen GLUE-Aufgaben funktioniert.
-
-{#if fw === 'tf'}
-
-Jetzt, da wir unseren Datensatz und einen DataCollator haben, müssen wir sie verbinden. Wir könnten Batches manuell laden und sortieren, aber das ist eine Menge Arbeit und wahrscheinlich auch nicht sehr sonderlich performant. Stattdessen gibt es eine einfache Methode, die dieses Problem performant löst: `to_tf_dataset()`. Dadurch wird ein `tf.data.Dataset` um den Datensatz gewickelt, mit einer optionalen Kollatierungsfunktion. `tf.data.Dataset` ist ein natives TensorFlow-Format, das Keras für `model.fit()` verwenden kann. Diese Methode kann einen 🤗-Datensatz ohne Umstände in ein fürs Training vorbereitetes Format konvertieren. Sehen wir es uns nun mit unserem Datensatz in Aktion an!
-
-```py
-tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
- columns=["attention_mask", "input_ids", "token_type_ids"],
- label_cols=["labels"],
- shuffle=True,
- collate_fn=data_collator,
- batch_size=8,
-)
-
-tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
- columns=["attention_mask", "input_ids", "token_type_ids"],
- label_cols=["labels"],
- shuffle=False,
- collate_fn=data_collator,
- batch_size=8,
-)
-```
-Und das was's! Wir können Datensätze in das nächste Kapitel mitnehmen, wo das Training nach all der harten Arbeit der Datenvorverarbeitung angenehm unkompliziert sein wird.
-
-{/if}
+
+
+# Vorbereitung der Daten
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+{#if fw === 'pt'}
+Wir fahren mit dem Beispiel aus dem [vorigen Kapitel](/course/chapter2) fort. Folgenderweise würden wir einen Sequenzklassifikator mit einem Batch in PyTorch trainieren:
+
+```python
+import torch
+from torch.optim import AdamW
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+# Genau wie vorher
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.", # Ich habe mein ganzes Leben auf einen HuggingFace-Kurs gewartet.
+ "This course is amazing!", # Dieser Kurs ist fantastisch!
+]
+batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
+
+# Dies ist neu
+batch["labels"] = torch.tensor([1, 1])
+
+optimizer = AdamW(model.parameters())
+loss = model(**batch).loss
+loss.backward()
+optimizer.step()
+```
+{:else}
+Wir fahren mit dem Beispiel aus dem [vorigen Kapitel](/course/chapter2) fort. Folgenderweise würden wir einen Sequenzklassifikator mit einem Batch in Tensorflow trainieren:
+
+```python
+import tensorflow as tf
+import numpy as np
+from transformers import AutoTokenizer, TFAutoModelForSequenceClassification
+
+# Genau wie vorher
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint)
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.", # Ich habe mein ganzes Leben auf einen HuggingFace-Kurs gewartet.
+ "This course is amazing!", # Dieser Kurs ist fantastisch!
+]
+batch = dict(tokenizer(sequences, padding=True, truncation=True, return_tensors="tf"))
+
+# Dies ist neu
+model.compile(optimizer="adam", loss="sparse_categorical_crossentropy")
+labels = tf.convert_to_tensor([1, 1])
+model.train_on_batch(batch, labels)
+```
+{/if}
+
+Natürlich würde das Training von Modellen mit nur zwei Sätzen keine sonderlich guten Ergebnisse liefern. Um bessere Ergebnisse zu erzielen, müssen wir einen größeren Datensatz vorbereiten.
+
+In diesem Abschnitt verwenden wir den MRPC-Datensatz (Microsoft Research Paraphrase Corpus) als Beispiel. Dieser wurde in einem [Paper](https://www.aclweb.org/anthology/I05-5002.pdf) von William B. Dolan und Chris Brockett veröffentlicht. Der Datensatz besteht aus insgesamt 5.801 Satzpaaren und enthält ein Label, das angibt, ob es sich bei einem Paar um Paraphrasen handelt (d.h. ob beide Sätze dasselbe bedeuten). Wir haben diesen Datensatz für dieses Kapitel ausgewählt, weil es sich um einen kleinen Datensatz handelt, sodass es einfach ist, während dem Training zu experimentieren.
+
+### Laden eines Datensatzes vom Hub
+
+{#if fw === 'pt'}
+
+{:else}
+
+{/if}
+
+Das Hub enthält nicht nur Modelle; Es hat auch mehrere Datensätze in vielen verschiedenen Sprachen. Du kannst die Datensätze [hier](https://huggingface.co/datasets) durchsuchen, und wir empfehlen, einen weiteren Datensatz zu laden und zu verarbeiten, sobald Sie diesen Abschnitt abgeschlossen haben (die Dokumentation befindet sich [hier](https://huggingface.co/docs/datasets/loading)). Aber jetzt konzentrieren wir uns auf den MRPC-Datensatz! Dies ist einer der 10 Datensätze, aus denen sich das [GLUE-Benchmark](https://gluebenchmark.com/) zusammensetzt. Dies ist ein akademisches Benchmark, das verwendet wird, um die Performance von ML-Modellen in 10 verschiedenen Textklassifizierungsaufgaben zu messen.
+
+Die Bibliothek 🤗 Datasets bietet einen leichten Befehl zum Herunterladen und Caching eines Datensatzes aus dem Hub. Wir können den MRPC-Datensatz wie folgt herunterladen:
+
+⚠️ ** Warnung** Stelle sicher, dass `datasets` installiert ist, indem du `pip install datasets` ausführst. Dann lade den MRPC-Datensatz und drucke ihn aus, um zu sehen, was er enthält.
+
+
+```py
+from datasets import load_dataset
+
+raw_datasets = load_dataset("glue", "mrpc")
+raw_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 3668
+ })
+ validation: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 408
+ })
+ test: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 1725
+ })
+})
+```
+
+Wie du sehen kannst, erhalten wir ein `DatasetDict`-Objekt, das die Trainingsdaten, die Validierungsdaten und die Testdaten enthält. Jedes Objekt enthält mehrere Spalten (`sentence1`, `sentence2`, `label` und `idx`) und eine unterschiedliche Anzahl an Zeilen, dies ist die Anzahl der Elemente in jedem Datensatz (also gibt es 3.668 Satzpaare in den Trainingsdaten, 408 in den Validierungsdaten und 1.725 in den Testdaten).
+
+Dieser Befehl lädt das Dataset herunter und speichert es im Cache, standardmäßig in *~/.cache/huggingface/dataset*. Wir Erinnern uns an Kapitel 2, dass der Cache-Ordner anpasst werden kann, indem man die Umgebungsvariable `HF_HOME` setzt.
+
+Wir können auf jedes Satzpaar in unserem `raw_datasets`-Objekt zugreifen, indem wir wie bei einem Dictionary einen Schlüsselwert als Index verwenden:
+
+```py
+raw_train_dataset = raw_datasets["train"]
+raw_train_dataset[0]
+```
+
+```python out
+{'idx': 0,
+ 'label': 1,
+ 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
+ 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
+```
+
+Wir stellen fest, dass die Labels bereits Ganzzahlen sind, sodass wir dort keine Vorverarbeitung durchführen müssen. Wir können die `features` von `raw_train_dataset` untersuchen, um zu erfahren, welche Ganzzahl welchem Label entspricht. Der folgende Befehl gibt uns den Variablentyp zurück:
+
+```py
+raw_train_dataset.features
+```
+
+```python out
+{'sentence1': Value(dtype='string', id=None),
+ 'sentence2': Value(dtype='string', id=None),
+ 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
+ 'idx': Value(dtype='int32', id=None)}
+```
+
+Hinter den Kulissen ist `label` vom Typ `ClassLabel`, und die Zuordnung von Ganzzahlen zum Labelnamen wird im Ordner *names* gespeichert. `0` entspricht `not_equivalent`, also "nicht äquivalent", und `1` entspricht `equivalent`, also "äquivalent".
+
+> [!TIP]
+> ✏️ **Probier es aus!** Sieh dir das Element 15 der Trainingsdaten und Element 87 des Validierungsdaten an. Was sind ihre Labels?
+
+### Vorverarbeitung eines Datensatzes
+
+{#if fw === 'pt'}
+
+{:else}
+
+{/if}
+
+Um den Datensatz vorzubereiten, müssen wir den Text in Zahlen umwandeln, die das Modell sinnvoll verarbeiten kann. Im [vorherigen Kapitel](/course/chapter2) haben wir gesehen, dass dies mit einem Tokenizer gemacht wird. Wir können den Tokenizer mit einem Satz oder einer Liste von Sätzen füttern, sodass wir die ersten und zweiten Sätze jedes Paares wie folgt direkt tokenisieren können:
+
+```py
+from transformers import AutoTokenizer
+
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
+tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
+```
+
+Wir können jedoch nicht einfach zwei Sequenzen an das Modell übergeben und eine Vorhersage erhalten, ob die beiden Sätze paraphrasiert sind oder nicht. Wir müssen die beiden Sequenzen als Paar behandeln und die entsprechende Vorverarbeitung anwenden. Glücklicherweise kann der Tokenizer auch ein Sequenzpaar nehmen und es so vorbereiten, wie es unser BERT-Modell erwartet:
+
+```py
+inputs = tokenizer("This is the first sentence.", "This is the second one.")
+inputs
+```
+
+```python out
+{
+ 'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
+ 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
+ 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+}
+```
+
+In [Kapitel 2](/course/chapter2) haben wir die Schlüsselwerte `input_ids` und `attention_mask` behandelt, allerdings haben wir es aufgeschoben, über `token_type_ids` zu sprechen. In diesem Beispiel teilt diese dem Modell mit, welcher Teil des Input der erste Satz und welcher der zweite Satz ist.
+
+> [!TIP]
+> ✏️ **Probier es aus!** Nimm Element 15 der Trainingsdaten und tokenisiere die beiden Sätze separat und als Paar. Wo liegt der Unterschied zwischen den beiden Ergebnissen?
+
+Wenn wir die IDs in `input_ids` zurück in Worte dekodieren:
+
+```py
+tokenizer.convert_ids_to_tokens(inputs["input_ids"])
+```
+
+dann bekommen wir:
+
+```python out
+['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
+```
+
+Wir sehen also wenn es zwei Sätze gibt, dass das Modell erwartet, dass die Inputs die Form "[CLS] Satz1 [SEP] Satz2 [SEP]" haben. Wenn wir dies mit den `token_type_ids` abgleichen, erhalten wir:
+
+```python out
+['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
+[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
+```
+
+Wie du sehen kannst, haben die Teile der Eingabe, die `[CLS] Satz1 [SEP]` entsprechen, alle eine Token-Typ-ID von `0`, während die anderen Teile, die `Satz2 [SEP]` entsprechen, alle einer Token-Typ-ID von `1` enthalten.
+
+Beachte, dass die Auswahl eines anderen Checkpoints nicht unbedingt die `token_type_ids` in Ihren tokenisierten Inputs haben (z.B. werden sie nicht zurückgegeben, wenn ein DistilBERT-Modell verwendet wird). Sie werden nur zurückgegeben, wenn das Modell weiß was damit zu tun ist, weil es die Toke-Typ-Ids während des Vortrainings gesehen hat.
+
+In diesem Fall ist BERT mit Token-Typ-IDs vortrainiert worden, und zusätzlich zu dem maskierten Sprachmodellierungsziel aud [Kapitel 1](/course/chapter1), hat es ein zusätzliches Vorhersageziel namens _next sentence prediction_ (d.h. Vorhersage des nächsten Satzes). Das Ziel dieser Aufgabe ist es, die Beziehung zwischen Satzpaaren zu modellieren.
+
+Bei der Vorhersage des nächsten Satzes werden dem Modell Satzpaare (mit zufällig maskierten Token) bereitgestellt und erwartet, vorherzusagen, ob auf den ersten Satz der zweite Satz folgt. Um die Aufgabe non-trivial zu machen, folgen sich die Hälfte der Sätze in dem Originaldokument, aus dem sie extrahiert wurden, aufeinander, und in der anderen Hälfte stammen die beiden Sätze aus zwei verschiedenen Dokumenten.
+
+Im Allgemeinen muss man sich keine Gedanken darüber machen, ob Ihre tokenisierten Inputs `token_type_ids` enthalten oder nicht: Solange du denselben Checkpoint für den Tokenizer und das Modell verwendest, ist alles in Ordnung, da der Tokenizer weiß, was er dem Modell bereitstellen soll.
+
+Nachdem wir nun gesehen haben, wie unser Tokenizer mit einem Satzpaar umgehen kann, können wir damit unseren gesamten Datensatz tokenisieren: Wie im [vorherigen Kapitel](/course/chapter2) können wir dem Tokenizer eine Liste von Satzpaaren einspeisen, indem du ihm die Liste der ersten Sätze und dann die Liste der zweiten Sätze gibst. Dies ist auch kompatibel mit den Optionen zum Padding und Trunkieren, die wir in [Kapitel 2](/course/chapter2) gesehen haben. Eine Möglichkeit, den Trainingsdatensatz vorzuverarbeiten, ist also:
+
+```py
+tokenized_dataset = tokenizer(
+ raw_datasets["train"]["sentence1"],
+ raw_datasets["train"]["sentence2"],
+ padding=True,
+ truncation=True,
+)
+```
+
+Das funktioniert gut, hat aber den Nachteil, dass ein Dictionary zurückgegeben wird (mit unseren Schlüsselwörtern `input_ids`, `attention_mask` und `token_type_ids` und Werten aus Listen von Listen). Es funktioniert auch nur, wenn du genügend RAM hast, um den gesamten Datensatz während der Tokenisierung zu im RAM zwischen zu speichern (während die Datensätze aus der Bibliothek 🤗 Datasets [Apache Arrow](https://arrow.apache.org/) Dateien sind, die auf der Festplatte gespeichert sind, sodass nur die gewünschten Samples im RAM geladen sind).
+
+Um die Daten als Datensatz zu speichern, verwenden wir die Methode [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map). Dies gewährt uns zusätzliche Flexibilität, wenn wir zusätzliche Vorverarbeitung als nur die Tokenisierung benötigen. Die `map()`-Methode funktioniert, indem sie eine Funktion auf jedes Element des Datensatzes anwendet, also definieren wir eine Funktion, die unsere Inputs tokenisiert:
+
+```py
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+```
+
+Diese Funktion nimmt ein Dictionary (wie die Elemente unseres Datensatzes) und gibt ein neues Dictionary mit den Schlüsselwerten `input_ids`, `attention_mask` und `token_type_ids` zurück. Beachte, dass es auch funktioniert, wenn das `example`-Dictionary mehrere Beispiele enthält (jeder Schlüsselwert als Liste von Sätzen), da der `Tokenizer`, wie zuvor gesehen, mit Listen von Satzpaaren arbeitet. Dadurch können wir die Option `batched=True` in unserem Aufruf von `map()` verwenden, was die Tokenisierung erheblich beschleunigt. Der `tokenizer` wurde in Rust geschriebenen und ist in der Bibliothek [🤗 Tokenizers](https://github.com/huggingface/tokenizers) verfügbar. Dieser Tokenizer kann sehr schnell arbeiten, wenn wir ihm viele Inputs auf einmal zum Verarbeiten geben. Note that we've left the `padding` argument out in our tokenization function for now.
+
+Beachte, dass wir das `padding`-Argument vorerst in unserer Tokenisierungsfunktion ausgelassen haben. Dies liegt daran, dass das Anwenden von Padding auf alle Elemente unserer Daten auf die maximale Länge nicht effizient ist: Es ist besser, die Proben aufzufüllen, wenn wir ein Batch erstellen, da wir dann nur auf die maximale Länge in diesem Batch auffüllen müssen und nicht auf die maximale Länge in den gesamten Datensatz. Dies kann viel Zeit und Rechenleistung sparen, besonders wenn die Eingaben stark variable Längen haben!
+
+So wenden wir die Tokenisierungsfunktion auf alle unsere Datensätze gleichzeitig an. In unserem Aufruf von `map` verwenden wir `batched=True`, damit die Funktion auf mehrere Elemente des Datensatzes gleichzeitig angewendet wird und nicht auf jedes Element separat. Dies ermöglicht eine schnellere Vorverarbeitung.
+
+```py
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+tokenized_datasets
+```
+
+Die Bibliothek 🤗 Datasets verarbeitet Datensätzen indem sie neue Felder hinzuzufügen, eines für jeden Schlüssel im Dictionary, der von der Vorverarbeitungsfunktion zurückgegeben wird:
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 3668
+ })
+ validation: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 408
+ })
+ test: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 1725
+ })
+})
+```
+
+Du kannst sogar Multiprocessing verwenden, wenn du die Vorverarbeitungsfunktion mit `map()` anwendest, indem du ein `num_proc`-Argument übergiebst. Wir haben dies hier nicht getan, weil die 🤗 Tokenizers-Bibliothek bereits mehrere Threads verwendet, um unsere Samples schneller zu tokenisieren. Wenn du keinen schnellen Tokenizer verwendest, der von dieser Bibliothek unterstützt wird, würde dies allerdings die Vorverarbeitung beschleunigen.
+
+Unsere `tokenize_function` gibt ein Dictionary mit den Schlüsselwerten `input_ids`, `attention_mask` und `token_type_ids` zurück, also werden diese drei Felder zu allen Splits unseres Datensatzes hinzugefügt. Beachte, dass wir auch vorhandene Felder ändern könnten, wenn unsere Vorverarbeitungsfunktion einen neuen Wert für einen vorhandenen Schlüsselwert in dem Datensatz zurückgegeben hätte, auf den wir `map()` angewendet haben.
+
+Zuletzt, müssen wir alle Beispiele auf die Länge des längsten Elements aufzufüllen, wenn wir Elemente zusammenfassen – eine Technik, die wir als *Dynamisches Padding* bezeichnen.
+
+### Dynamisches Padding
+
+
+
+{#if fw === 'pt'}
+
+Die Funktion, die für das Zusammenstellen von Samples innerhalb eines Batches verantwortlich ist, wird als *Collate-Funktion* bezeichnet. Es ist ein Argument, das du übergeben kannst, wenn du einen `DataLoader` baust, wobei es standardmäßig eine Funktion ist, die die Daten in PyTorch-Tensoren umwandelt und zusammenfügt (rekursiv wenn die Elemente Listen, Tupel oder Dictionaries sind). Dies ist in unserem Fall nicht möglich, da die Inputs nicht alle gleich groß sind. Das Padding haben wir bewusst aufgeschoben, um es bei jedem Batch nur bei Bedarf anzuwenden und überlange Inputs mit massivem Padding zu vermeiden. Dies beschleunigt das Training zwar, aber beachte, dass das Training auf einer TPU Probleme verursachen kann – TPUs bevorzugen feste Formen, auch wenn das ein zusätzliches Padding erfordert.
+
+{:else}
+
+Die Funktion, die für das Zusammenstellen von Samples innerhalb eines Batches verantwortlich ist, wird als *Collate-Funktion* bezeichnet. Es ist ein Argument, das du übergeben kannst, wenn du einen `DataLoader` baust, wobei es standardmäßig eine Funktion ist, die die Daten in tf.Tensor umwandelt und zusammenfügt (rekursiv wenn die Elemente Listen, Tupel oder Dictionaries sind). Dies ist in unserem Fall nicht möglich, da die Inputs nicht alle gleich groß sind. Das Padding haben wir bewusst aufgeschoben, um es bei jedem Batch nur bei Bedarf anzuwenden und überlange Inputs mit massivem Padding zu vermeiden. Dies beschleunigt das Training zwar, aber beachte, dass das Training auf einer TPU Probleme verursachen kann – TPUs bevorzugen feste Formen, auch wenn das ein zusätzliches Padding erfordert.
+
+{/if}
+
+In der Praxis müssen wir eine Collate-Funktion definieren, die die korrekte Menge an Padding auf die Elemente des Datensatzes anwendet, die wir in einem Batch haben möchten. Glücklicherweise stellt uns die 🤗 Transformers-Bibliothek über `DataCollatorWithPadding` eine solche Funktion zur Verfügung. Wenn sie instanziert wird, braucht es einen Tokenizer (um zu wissen, welches Padding-token verwendet werden soll und ob das Modell erwartet, dass sich das Padding links oder rechts von den Inputs befindet) und übernimmt alles was wir brauchen:
+
+{#if fw === 'pt'}
+```py
+from transformers import DataCollatorWithPadding
+
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+{:else}
+```py
+from transformers import DataCollatorWithPadding
+
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
+```
+{/if}
+
+Um dieses neue Werkzeug zu testen, nehmen wir einige Elemente aus den Trainingsdaten, die wir als Batch verwenden möchten. Hier entfernen wir die Spalten `idx`, `sentence1` und `sentence2`, da sie nicht benötigt werden und Strings enthalten (wir können keine Tensoren mit Strings erstellen) und sehen uns die Länge jedes Eintrags im Batch an:
+
+```py
+samples = tokenized_datasets["train"][:8]
+samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
+[len(x) for x in samples["input_ids"]]
+```
+
+```python out
+[50, 59, 47, 67, 59, 50, 62, 32]
+```
+
+Wenig überraschen erhalten wir Samples unterschiedlicher Länge von 32 bis 67. Dynamisches Padding bedeutet, dass die Elemente in diesem Batch alle auf eine Länge von 67 aufgefüllt werden, die maximale Länge innerhalb des Batches. Ohne dynamisches Auffüllen müssten alle Einträge auf die maximale Länge im gesamten Datensatz oder auf die maximale Länge die das Modell akzeptiert, aufgefüllt werden. Lass uns noch einmal überprüfen, ob unser `data_collator` den Stapel dynamisch richtig auffüllt:
+
+```py
+batch = data_collator(samples)
+{k: v.shape for k, v in batch.items()}
+```
+
+{#if fw === 'tf'}
+
+```python out
+{'attention_mask': TensorShape([8, 67]),
+ 'input_ids': TensorShape([8, 67]),
+ 'token_type_ids': TensorShape([8, 67]),
+ 'labels': TensorShape([8])}
+```
+
+{:else}
+
+```python out
+{'attention_mask': torch.Size([8, 67]),
+ 'input_ids': torch.Size([8, 67]),
+ 'token_type_ids': torch.Size([8, 67]),
+ 'labels': torch.Size([8])}
+```
+
+Das sieht gut aus! Jetzt, da wir vom Rohtext zu Batches übergegangen sind, mit denen unser Modell umgehen kann, sind wir bereit zum fein-tunen!
+
+{/if}
+
+> [!TIP]
+> ✏️ **Probier es aus!** Repliziere die Vorverarbeitung auf dem GLUE SST-2-Datensatz. Es ist ein bisschen anders, da es aus einzelnen Sätzen statt aus Paaren besteht, aber der Rest von dem, was wir gemacht haben, sollte gleich aussehen. Alternative wäre eine schwierigere Herausforderung, eine Vorverarbeitungsfunktion zu schreiben, die bei allen GLUE-Aufgaben funktioniert.
+
+{#if fw === 'tf'}
+
+Jetzt, da wir unseren Datensatz und einen DataCollator haben, müssen wir sie verbinden. Wir könnten Batches manuell laden und sortieren, aber das ist eine Menge Arbeit und wahrscheinlich auch nicht sehr sonderlich performant. Stattdessen gibt es eine einfache Methode, die dieses Problem performant löst: `to_tf_dataset()`. Dadurch wird ein `tf.data.Dataset` um den Datensatz gewickelt, mit einer optionalen Kollatierungsfunktion. `tf.data.Dataset` ist ein natives TensorFlow-Format, das Keras für `model.fit()` verwenden kann. Diese Methode kann einen 🤗-Datensatz ohne Umstände in ein fürs Training vorbereitetes Format konvertieren. Sehen wir es uns nun mit unserem Datensatz in Aktion an!
+
+```py
+tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
+ columns=["attention_mask", "input_ids", "token_type_ids"],
+ label_cols=["labels"],
+ shuffle=True,
+ collate_fn=data_collator,
+ batch_size=8,
+)
+
+tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
+ columns=["attention_mask", "input_ids", "token_type_ids"],
+ label_cols=["labels"],
+ shuffle=False,
+ collate_fn=data_collator,
+ batch_size=8,
+)
+```
+Und das was's! Wir können Datensätze in das nächste Kapitel mitnehmen, wo das Training nach all der harten Arbeit der Datenvorverarbeitung angenehm unkompliziert sein wird.
+
+{/if}
diff --git a/chapters/de/chapter3/3.mdx b/chapters/de/chapter3/3.mdx
index ef20299a4..b248a578c 100644
--- a/chapters/de/chapter3/3.mdx
+++ b/chapters/de/chapter3/3.mdx
@@ -1,166 +1,166 @@
-
-
-# Fine-tuning eine Modells mit der Trainer API
-
-
-
-
-
-🤗 Transformers stellt eine `Trainer`-Klasse bereit, mit der du Modelle auf deinen Datensätzen fein-tunen kannst. Nachdem die Datenverarbeitung im letzten Abschnitt abgeschlossen ist, bleiben nur noch wenige Schritte, um den `Trainer` zu definieren. Der schwierigste Teil ist die Vorbereitung der Umgebung um `Trainer.train()` auszuführen, da dies auf einer CPU sehr langsam läuft. Wenn keine GPU verfügbar ist, kannst du bei [Google Colab] (https://colab.research.google.com/) auf kostenlose GPUs oder TPUs zugreifen.
-
-In den folgenden Code-Beispielen wird davon ausgegangen, dass du die Beispiele aus dem vorherigen Abschnitt bereits ausgeführt hast. Hier ist eine kurze Zusammenfassung, die dir zeigt, was erwartet wird:
-
-```py
-from datasets import load_dataset
-from transformers import AutoTokenizer, DataCollatorWithPadding
-
-raw_datasets = load_dataset("glue", "mrpc")
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-
-def tokenize_function(example):
- return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
-
-
-tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
-```
-
-### Training
-
-Als erstes müssen wir eine Klasse `TrainingArguments` definieren, die alle Hyperparameter enthält, die der `Trainer` für das Training und die Evaluation verwendet. Das einzige Argument das hier angegeben werden muss, ist ein Verzeichnis in dem das trainierte Modell sowie die Checkpoints gespeichert werden. Für alles andere können die Standardeinstellungen verwendet werden. Diese sollten für ein grundlegendes Fein-tunen ausreichen.
-
-```py
-from transformers import TrainingArguments
-
-training_args = TrainingArguments("test-trainer")
-```
-
-> [!TIP]
-> 💡 Wenn du dein Modell während des Trainings automatisch in das Hub hochladen möchtest, kann in `TrainingArguments` das Argument `push_to_hub=True` angegeben werden. Darüber erfahren wir in [Kapitel 4](/course/chapter4/3) mehr.
-
-Der zweite Schritt ist die Definition unseres Modells. Wie im [vorherigen Kapitel](/course/chapter2) verwenden wir die Klasse `AutoModelForSequenceClassification` mit zwei Labels:
-
-```py
-from transformers import AutoModelForSequenceClassification
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-```
-
-Du wirst feststellen, dass du im Gegensatz zu [Kapitel 2](/course/chapter2) eine Warnung erhältst, nachdem du dieses vortrainierte Modell instanziiert hast. Der Grund dafür ist, dass BERT nicht auf die Klassifizierung von Satzpaaren vortrainiert wurde. Deshalb wurde der Kopf des vortrainierten Modells verworfen und stattdessen ein neuer Kopf hinzugefügt, der für die Klassifizierung von Sequenzen geeignet ist. Diese Warnungen weisen darauf hin, dass Teile der Gewichtung nicht verwendet wurden (die Gewichte für den verworfenen Kopf) und dass einige andere zufällig initialisiert wurden (die Gewichte für den neuen Kopf). Abschließend werden wir aufgefordert, das Modell zu trainieren, und genau das werden wir jetzt tun.
-
-Sobald wir unser Modell haben, können wir einen `Trainer` definieren, indem wir alle bisher erstellten Objekte übergeben - das `Modell`, die `training_args`, die Trainings- und Validierungsdaten, unseren `data_collator` und unseren `tokenizer`:
-
-```py
-from transformers import Trainer
-
-trainer = Trainer(
- model,
- training_args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- tokenizer=tokenizer,
-)
-```
-
-Merke: Wenn der `tokenizer` übergeben wird, wie wir es hier getan haben, wird der vom `Trainer` verwendete `data_collator` standardmäßig ein `DataCollatorWithPadding` sein, wie er zuvor definiert wurde. Deshalb kannst du die Zeile `data_collator=data_collator` in diesem Aufruf weglassen. Unabhängig davon war es trotzdem wichtig, diesen Teil der Verarbeitung in Abschnitt 2 zu zeigen!
-
-Um das Modell auf unserem Datensatz fein-tunen zu können, müssen wir nur die Methode `train()` unseres `Trainers` aufrufen:
-
-```py
-trainer.train()
-```
-
-Dadurch wird das Fein-tunen gestartet (was auf einer GPU ein paar Minuten dauern sollte) und der Trainingsverlust wird alle 500 Schritte gemeldet. Es wird jedoch nicht zurückgegeben, wie gut (oder schlecht) das Modell funktioniert. Dies liegt an folgenden Punkten:
-
-1. Wir haben dem `Trainer` nicht mitgeteilt die Performance in der Trainingsschleife auszuwerten, indem wir `evaluation_strategy` entweder auf `"steps"` (alle `eval_steps` auswerten) oder `"epoch"` (am Ende jeder Epoche evaluieren) gesetzt haben.
-2. Wir haben dem `Trainer` keine Funktion `compute_metrics()` zur Verfügung gestellt, um während der Evaluation eine Metrik zu berechnen (sonst hätte die Evaluation nur den Verlust ausgegeben, was keine sehr intuitive Zahl ist).
-
-
-### Evaluation
-
-Im Folgenden wird gezeigt, wie wir eine `compute_metrics()`-Funktion erstellen und sie beim nächsten Training verwenden können. Die Funktion muss ein `EvalPrediction`-Objekt (ein bennantes Tupel mit einem `predictions`-Feld und einem `label_ids`-Feld) annehmen und ein Dictionary zurückgeben, das Strings auf Floats abbildet (die Strings sind die Namen der zurückgegebenen Metriken und die Floats ihre zugehörigen Werte). Um Vorhersagen von unserem Modell zu erhalten, können wir den Befehl "Trainer.predict()" verwenden:
-
-```py
-predictions = trainer.predict(tokenized_datasets["validation"])
-print(predictions.predictions.shape, predictions.label_ids.shape)
-```
-
-```python out
-(408, 2) (408,)
-```
-
-Die Ausgabe der `predict()`-Methode ist ein weiteres benanntes Tupel mit drei Feldern: `predictions`, `label_ids` und `metrics`. Das Feld `metrics` enthält den Verlust des übergebenen Datensatzes sowie Zeitangaben dazu, wie lange die Vorhersage insgesamt und im Durchschnitt gedauert hat. Sobald wir unsere Funktion `compute_metrics()` fertiggestellt haben und sie an den `Trainer` übergeben, enthält dieses Feld auch die von der `compute_metrics()`-Funktion zurückgegebenen Metriken.
-
-Die Vorhersagen in `predictions` sind ein zweidimensionales Array mit der Form 408 x 2 (408 ist die Anzahl der Elemente unseres Datensatzes). Das sind die Logits für jedes Element des Datensatzes, das wir an `predict()` übergeben haben (siehe [vorheriges Kapitel](/course/chapter2) dass alle Transformer Modelle Logits zurückgeben). Um diese in Vorhersagen umzuwandeln, die wir mit den Labels vergleichen können, müssen wir den Index mit dem höchsten Wert auf der zweiten Achse nehmen:
-
-```py
-import numpy as np
-
-preds = np.argmax(predictions.predictions, axis=-1)
-```
-
-Jetzt können wir diese Vorhersagen in `preds` mit den Labels vergleichen. Wir greifen auf die Metriken aus der 🤗 Bibliothek [Evaluate](https://github.com/huggingface/evaluate/) zurück, um unsere Funktion `compute_metric()` zu erstellen. Die mit dem MRPC-Datensatz verbundenen Metriken können genauso einfach geladen werden, wie wir den Datensatz geladen haben, diesmal mit der Funktion `evaluate.load()`. Das zurückgegebene Objekt verfügt über eine Berechnungsmethode, mit der wir die Metrik auswerten können:
-
-```py
-import evaluate
-
-metric = evaluate.load("glue", "mrpc")
-metric.compute(predictions=preds, references=predictions.label_ids)
-```
-
-```python out
-{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
-```
-
-Die genauen Ergebnisse können variieren, da die zufällige Initialisierung des Modellkopfes den Optimierungsverlauf und damit die Metriken verändern kann. Hier hat das Modell eine Genauigkeit von 85,78 % über die Validierungsdaten und eine F1-Maß von 89,97 erreicht hat. Dies sind die beiden Kennzahlen, die zur Bewertung der Ergebnisse des MRPC-Datensatzes für den GLUE-Benchmark verwendet werden. In der Tabelle im [BERT-Paper] (https://arxiv.org/pdf/1810.04805.pdf) wird für das Basismodell ein F1-Maß von 88,9 angegeben. Das Paper hat das `uncased` Modell verwendet, während wir derzeit das `cased` Modell verwenden, was das bessere Ergebnis erklärt.
-
-Zusammenfassend ergibt das unsere Funktion `compute_metrics()`:
-
-```py
-def compute_metrics(eval_preds):
- metric = evaluate.load("glue", "mrpc")
- logits, labels = eval_preds
- predictions = np.argmax(logits, axis=-1)
- return metric.compute(predictions=predictions, references=labels)
-```
-
-Um diese Funktion in Aktion zu sehen, definieren wir einen neuen `Trainer` mit der Funktion "compute_metrics()", um am Ende jeder Epoche Metriken zu melden:
-
-```py
-training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-
-trainer = Trainer(
- model,
- training_args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- tokenizer=tokenizer,
- compute_metrics=compute_metrics,
-)
-```
-
-Hier ein Hinweis, dass wir ein neues `TrainingArguments` errstellen, dessen `evaluation_strategy` auf `"epoch"` gesetzt ist, und ein neues Modell definieren - andernfalls würden wir nur das Training des momentanen Modells fortführen, das wir bereits trainiert haben. Um einen neuen Trainingslauf zu starten, führen wir folgendes aus:
-
-```
-trainer.train()
-```
-
-Nun werden am Ende jeder Epoche zusätzlich zu den Trainingsverlusten auch die Validierungsverluste und -metriken gemeldet. Auch hier kann die Genauigkeit/F1-Maß aufgrund der zufälligen Initialisierung des Modells zu unserem Beispiel variieren, aber sie sollte in etwa gleich sein.
-
-Der `Trainer` funktioniert sofort auf mehreren GPUs oder TPUs und bietet zahlreiche Optionen, wie z. B. Training mit gemischter Genauigkeit (verwende `fp16 = True` in deinen Trainingsargumenten). In Kapitel 10 gehen wir auf alle Funktionen ein, die die `Trainer`-Klasse bereitstellt.
-
-Damit ist die Einführung in das Fein-tunen mit der `Trainer` API abgeschlossen. Beispiele für die gängigsten CL-Aufgaben werden in Kapitel 7 gegeben, aber jetzt schauen wir uns erst einmal an, wie man das Gleiche in PyTorch bewerkstelligen kann.
-
-> [!TIP]
-> ✏️ **Probier es aus!** Fein-tune ein Modell mit dem GLUE SST-2 Datensatz, indem du die Datenverarbeitung aus Abschnitt 2 verwendest.
-
+
+
+# Fine-tuning eine Modells mit der Trainer API
+
+
+
+
+
+🤗 Transformers stellt eine `Trainer`-Klasse bereit, mit der du Modelle auf deinen Datensätzen fein-tunen kannst. Nachdem die Datenverarbeitung im letzten Abschnitt abgeschlossen ist, bleiben nur noch wenige Schritte, um den `Trainer` zu definieren. Der schwierigste Teil ist die Vorbereitung der Umgebung um `Trainer.train()` auszuführen, da dies auf einer CPU sehr langsam läuft. Wenn keine GPU verfügbar ist, kannst du bei [Google Colab] (https://colab.research.google.com/) auf kostenlose GPUs oder TPUs zugreifen.
+
+In den folgenden Code-Beispielen wird davon ausgegangen, dass du die Beispiele aus dem vorherigen Abschnitt bereits ausgeführt hast. Hier ist eine kurze Zusammenfassung, die dir zeigt, was erwartet wird:
+
+```py
+from datasets import load_dataset
+from transformers import AutoTokenizer, DataCollatorWithPadding
+
+raw_datasets = load_dataset("glue", "mrpc")
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+
+
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+
+### Training
+
+Als erstes müssen wir eine Klasse `TrainingArguments` definieren, die alle Hyperparameter enthält, die der `Trainer` für das Training und die Evaluation verwendet. Das einzige Argument das hier angegeben werden muss, ist ein Verzeichnis in dem das trainierte Modell sowie die Checkpoints gespeichert werden. Für alles andere können die Standardeinstellungen verwendet werden. Diese sollten für ein grundlegendes Fein-tunen ausreichen.
+
+```py
+from transformers import TrainingArguments
+
+training_args = TrainingArguments("test-trainer")
+```
+
+> [!TIP]
+> 💡 Wenn du dein Modell während des Trainings automatisch in das Hub hochladen möchtest, kann in `TrainingArguments` das Argument `push_to_hub=True` angegeben werden. Darüber erfahren wir in [Kapitel 4](/course/chapter4/3) mehr.
+
+Der zweite Schritt ist die Definition unseres Modells. Wie im [vorherigen Kapitel](/course/chapter2) verwenden wir die Klasse `AutoModelForSequenceClassification` mit zwei Labels:
+
+```py
+from transformers import AutoModelForSequenceClassification
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+```
+
+Du wirst feststellen, dass du im Gegensatz zu [Kapitel 2](/course/chapter2) eine Warnung erhältst, nachdem du dieses vortrainierte Modell instanziiert hast. Der Grund dafür ist, dass BERT nicht auf die Klassifizierung von Satzpaaren vortrainiert wurde. Deshalb wurde der Kopf des vortrainierten Modells verworfen und stattdessen ein neuer Kopf hinzugefügt, der für die Klassifizierung von Sequenzen geeignet ist. Diese Warnungen weisen darauf hin, dass Teile der Gewichtung nicht verwendet wurden (die Gewichte für den verworfenen Kopf) und dass einige andere zufällig initialisiert wurden (die Gewichte für den neuen Kopf). Abschließend werden wir aufgefordert, das Modell zu trainieren, und genau das werden wir jetzt tun.
+
+Sobald wir unser Modell haben, können wir einen `Trainer` definieren, indem wir alle bisher erstellten Objekte übergeben - das `Modell`, die `training_args`, die Trainings- und Validierungsdaten, unseren `data_collator` und unseren `tokenizer`:
+
+```py
+from transformers import Trainer
+
+trainer = Trainer(
+ model,
+ training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ tokenizer=tokenizer,
+)
+```
+
+Merke: Wenn der `tokenizer` übergeben wird, wie wir es hier getan haben, wird der vom `Trainer` verwendete `data_collator` standardmäßig ein `DataCollatorWithPadding` sein, wie er zuvor definiert wurde. Deshalb kannst du die Zeile `data_collator=data_collator` in diesem Aufruf weglassen. Unabhängig davon war es trotzdem wichtig, diesen Teil der Verarbeitung in Abschnitt 2 zu zeigen!
+
+Um das Modell auf unserem Datensatz fein-tunen zu können, müssen wir nur die Methode `train()` unseres `Trainers` aufrufen:
+
+```py
+trainer.train()
+```
+
+Dadurch wird das Fein-tunen gestartet (was auf einer GPU ein paar Minuten dauern sollte) und der Trainingsverlust wird alle 500 Schritte gemeldet. Es wird jedoch nicht zurückgegeben, wie gut (oder schlecht) das Modell funktioniert. Dies liegt an folgenden Punkten:
+
+1. Wir haben dem `Trainer` nicht mitgeteilt die Performance in der Trainingsschleife auszuwerten, indem wir `evaluation_strategy` entweder auf `"steps"` (alle `eval_steps` auswerten) oder `"epoch"` (am Ende jeder Epoche evaluieren) gesetzt haben.
+2. Wir haben dem `Trainer` keine Funktion `compute_metrics()` zur Verfügung gestellt, um während der Evaluation eine Metrik zu berechnen (sonst hätte die Evaluation nur den Verlust ausgegeben, was keine sehr intuitive Zahl ist).
+
+
+### Evaluation
+
+Im Folgenden wird gezeigt, wie wir eine `compute_metrics()`-Funktion erstellen und sie beim nächsten Training verwenden können. Die Funktion muss ein `EvalPrediction`-Objekt (ein bennantes Tupel mit einem `predictions`-Feld und einem `label_ids`-Feld) annehmen und ein Dictionary zurückgeben, das Strings auf Floats abbildet (die Strings sind die Namen der zurückgegebenen Metriken und die Floats ihre zugehörigen Werte). Um Vorhersagen von unserem Modell zu erhalten, können wir den Befehl "Trainer.predict()" verwenden:
+
+```py
+predictions = trainer.predict(tokenized_datasets["validation"])
+print(predictions.predictions.shape, predictions.label_ids.shape)
+```
+
+```python out
+(408, 2) (408,)
+```
+
+Die Ausgabe der `predict()`-Methode ist ein weiteres benanntes Tupel mit drei Feldern: `predictions`, `label_ids` und `metrics`. Das Feld `metrics` enthält den Verlust des übergebenen Datensatzes sowie Zeitangaben dazu, wie lange die Vorhersage insgesamt und im Durchschnitt gedauert hat. Sobald wir unsere Funktion `compute_metrics()` fertiggestellt haben und sie an den `Trainer` übergeben, enthält dieses Feld auch die von der `compute_metrics()`-Funktion zurückgegebenen Metriken.
+
+Die Vorhersagen in `predictions` sind ein zweidimensionales Array mit der Form 408 x 2 (408 ist die Anzahl der Elemente unseres Datensatzes). Das sind die Logits für jedes Element des Datensatzes, das wir an `predict()` übergeben haben (siehe [vorheriges Kapitel](/course/chapter2) dass alle Transformer Modelle Logits zurückgeben). Um diese in Vorhersagen umzuwandeln, die wir mit den Labels vergleichen können, müssen wir den Index mit dem höchsten Wert auf der zweiten Achse nehmen:
+
+```py
+import numpy as np
+
+preds = np.argmax(predictions.predictions, axis=-1)
+```
+
+Jetzt können wir diese Vorhersagen in `preds` mit den Labels vergleichen. Wir greifen auf die Metriken aus der 🤗 Bibliothek [Evaluate](https://github.com/huggingface/evaluate/) zurück, um unsere Funktion `compute_metric()` zu erstellen. Die mit dem MRPC-Datensatz verbundenen Metriken können genauso einfach geladen werden, wie wir den Datensatz geladen haben, diesmal mit der Funktion `evaluate.load()`. Das zurückgegebene Objekt verfügt über eine Berechnungsmethode, mit der wir die Metrik auswerten können:
+
+```py
+import evaluate
+
+metric = evaluate.load("glue", "mrpc")
+metric.compute(predictions=preds, references=predictions.label_ids)
+```
+
+```python out
+{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
+```
+
+Die genauen Ergebnisse können variieren, da die zufällige Initialisierung des Modellkopfes den Optimierungsverlauf und damit die Metriken verändern kann. Hier hat das Modell eine Genauigkeit von 85,78 % über die Validierungsdaten und eine F1-Maß von 89,97 erreicht hat. Dies sind die beiden Kennzahlen, die zur Bewertung der Ergebnisse des MRPC-Datensatzes für den GLUE-Benchmark verwendet werden. In der Tabelle im [BERT-Paper] (https://arxiv.org/pdf/1810.04805.pdf) wird für das Basismodell ein F1-Maß von 88,9 angegeben. Das Paper hat das `uncased` Modell verwendet, während wir derzeit das `cased` Modell verwenden, was das bessere Ergebnis erklärt.
+
+Zusammenfassend ergibt das unsere Funktion `compute_metrics()`:
+
+```py
+def compute_metrics(eval_preds):
+ metric = evaluate.load("glue", "mrpc")
+ logits, labels = eval_preds
+ predictions = np.argmax(logits, axis=-1)
+ return metric.compute(predictions=predictions, references=labels)
+```
+
+Um diese Funktion in Aktion zu sehen, definieren wir einen neuen `Trainer` mit der Funktion "compute_metrics()", um am Ende jeder Epoche Metriken zu melden:
+
+```py
+training_args = TrainingArguments("test-trainer", evaluation_strategy="epoch")
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+
+trainer = Trainer(
+ model,
+ training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ tokenizer=tokenizer,
+ compute_metrics=compute_metrics,
+)
+```
+
+Hier ein Hinweis, dass wir ein neues `TrainingArguments` errstellen, dessen `evaluation_strategy` auf `"epoch"` gesetzt ist, und ein neues Modell definieren - andernfalls würden wir nur das Training des momentanen Modells fortführen, das wir bereits trainiert haben. Um einen neuen Trainingslauf zu starten, führen wir folgendes aus:
+
+```
+trainer.train()
+```
+
+Nun werden am Ende jeder Epoche zusätzlich zu den Trainingsverlusten auch die Validierungsverluste und -metriken gemeldet. Auch hier kann die Genauigkeit/F1-Maß aufgrund der zufälligen Initialisierung des Modells zu unserem Beispiel variieren, aber sie sollte in etwa gleich sein.
+
+Der `Trainer` funktioniert sofort auf mehreren GPUs oder TPUs und bietet zahlreiche Optionen, wie z. B. Training mit gemischter Genauigkeit (verwende `fp16 = True` in deinen Trainingsargumenten). In Kapitel 10 gehen wir auf alle Funktionen ein, die die `Trainer`-Klasse bereitstellt.
+
+Damit ist die Einführung in das Fein-tunen mit der `Trainer` API abgeschlossen. Beispiele für die gängigsten CL-Aufgaben werden in Kapitel 7 gegeben, aber jetzt schauen wir uns erst einmal an, wie man das Gleiche in PyTorch bewerkstelligen kann.
+
+> [!TIP]
+> ✏️ **Probier es aus!** Fein-tune ein Modell mit dem GLUE SST-2 Datensatz, indem du die Datenverarbeitung aus Abschnitt 2 verwendest.
+
diff --git a/chapters/de/chapter3/3_tf.mdx b/chapters/de/chapter3/3_tf.mdx
index 4d4cbb18b..f4e9f80c7 100644
--- a/chapters/de/chapter3/3_tf.mdx
+++ b/chapters/de/chapter3/3_tf.mdx
@@ -1,180 +1,180 @@
-
-
-# Modell mit Keras fein-tunen
-
-
-
-Wenn du die Datenvorverarbeitung im letzten Abschnitt abgeschlossen hast, brauchst es nur noch wenige Schritte, um das Modell zu trainieren. Beachte jedoch, dass der Befehl `model.fit()` auf einer CPU sehr langsam läuft. Wenn du keinen GPU hast, kannst du auf [Google Colab] (https://colab.research.google.com/) kostenlos auf GPUs und TPUs zugreifen.
-
-Bei den folgenden Codebeispielen wird davon ausgegangen, dass du die Beispiele aus dem vorherigen Abschnitt bereits ausgeführt hast. Hier ist eine kurze Zusammenfassung, die aufzeigt was erwartet wird:
-
-```py
-from datasets import load_dataset
-from transformers import AutoTokenizer, DataCollatorWithPadding
-import numpy as np
-
-raw_datasets = load_dataset("glue", "mrpc")
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-
-def tokenize_function(example):
- return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
-
-
-tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
-
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
-
-tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
- columns=["attention_mask", "input_ids", "token_type_ids"],
- label_cols=["labels"],
- shuffle=True,
- collate_fn=data_collator,
- batch_size=8,
-)
-
-tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
- columns=["attention_mask", "input_ids", "token_type_ids"],
- label_cols=["labels"],
- shuffle=False,
- collate_fn=data_collator,
- batch_size=8,
-)
-```
-
-### Training
-
-Tensorflow Modelle, die von 🤗 Transformers importiert werden, sind bereits Keras Modelle. Hier ist eine kurze Einführung in Keras.
-
-
-
-Sobald wir die Daten haben, braucht es nur noch sehr wenig Arbeit, um mit dem Training zu beginnen.
-
-
-
-Wie im [vorherigen Kapitel](/course/chapter2) verwenden wir die Klasse `TFAutoModelForSequenceClassification` mit zwei Labels:
-
-```py
-from transformers import TFAutoModelForSequenceClassification
-
-model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-```
-
-Im Gegensatz zu [Kapitel 2](/course/chapter2) wird eine Warnung angezeigt, nachdem das Modell instanziiert wurde. Das liegt daran, dass BERT nicht auf die Klassifizierung von Satzpaaren vortrainiert wurde. Deshalb wurde der Kopf des vortrainierten Modells verworfen und stattdessen ein neuer Kopf eingefügt, der für die Klassifizierung von Sequenzen geeignet ist. Die Warnungen zeigen an, dass Teil der Gewichtung nicht verwendet wurden (die Gewichte für den verworfenen Kopf) und dass einige andere zufällig initialisiert wurden (die Gewichte für den neuen Kopf). Abschließend wirst du aufgefordert, das Modell zu trainieren, und genau das werden wir jetzt tun.
-
-Um das Modell mit unserem Datensatz fein-tunen zu können, müssen wir das Modell `kompilieren()` und unsere Daten an die `fit()`-Methode übergeben. Damit wird das Fein-tuning gestartet (dies sollte auf einer GPU ein paar Minuten dauern) und der Trainingsverlust sowie der Validierungsverlust am Ende jeder Epoche gemeldet.
-
-> [!TIP]
-> 🤗 Transformer Modelle haben eine besondere Fähigkeit, die die meisten Keras Modelle nicht haben - sie können automatisch einen geeigneten Verlust verwenden, der intern berechnet wird. Dieser Verlust wird standardmäßig verwendet, wenn in `compile()` kein Verlustargument angegeben wird. Um den internen Verlust zu verwenden, musst du deine Labels als Teil des Input übergeben und nicht als separates Label, wie es normalerweise bei Keras-Modellen der Fall ist. Beispiele dafür gibt es in Teil 2 des Kurses, wobei die Definition der richtigen Verlustfunktion schwierig sein kann. Für die Klassifizierung von Sequenzen eignet sich jedoch eine der Standardverlustfunktionen von Keras, die wir hier verwenden werden.
-
-```py
-from tensorflow.keras.losses import SparseCategoricalCrossentropy
-
-model.compile(
- optimizer="adam",
- loss=SparseCategoricalCrossentropy(from_logits=True),
- metrics=["accuracy"],
-)
-model.fit(
- tf_train_dataset,
- validation_data=tf_validation_dataset,
-)
-```
-
-> [!WARNING]
-> Hier gibt es einen sehr häufigen Stolperstein - du *kannst* Keras einfach den Namen des Verlusts als String übergeben, aber standardmäßig geht Keras davon aus, dass du bereits einen Softmax auf die Outputs angewendet hast. Viele Modelle geben jedoch die Werte direkt vor der Anwendung des Softmax als *Logits* aus. Hier ist es wichtig der Keras Verlustfunktion mitzuteilen, dass unser Modell genau diess tut, und das geht nur indem sie direkt aufgerufen wird, und nicht über den Namen mit einem String.
-
-
-### Verbesserung der Trainingsperformance
-
-
-
-Wenn du den obigen Code ausprobierst, läuft er zwar, aber du wirst feststellen, dass der Verlust nur langsam oder sporadisch zurückgeht. Die Ursache hierfür ist die *Lernrate*. Wenn der Namen eines Optimierers als String an Keras übergeben wird, initialisiert Keras diesen Optimierer mit Standardwerten für alle Parameter, einschließlich der Lernrate. Aus langjähriger Erfahrung wissen wir, dass Transformer Modelle von einer wesentlich niedrigeren Lernrate profitieren als der Standardwert für Adam. Dieser Standardwert liegt bei 1e-3, auch geschrieben als 10 hoch -3 oder 0,001. Für Transformer ist 5e-5 (0,00005), was etwa zwanzigmal niedriger ist, ist ein viel besserer Ausgangspunkt.
-
-Zusätzlich zur Senkung der Lernrate haben wir noch einen zweiten Trick in petto: Wir können die Lernrate langsam im Laufe des Trainings verringern. In der Literatur wird dies manchmal als *Decay* oder *Annealing* der Lernrate bezeichnet. In Keras kannst das am besten mit dem *Lernraten-Scheduler* umgesetzt werden. Ein guter Scheduler ist `PolynomialDecay` - trotz des Namens lässt er die Lernrate in den Standardeinstellungen einfach linear vom Anfangswert bis zum Endwert abfallen. Dies ist genau was wir wollen. Um einen Scheduler richtig zu nutzen, müssen wir ihm allerdings sagen, wie lange das Training dauern soll. Das berechnen wir im Folgenden als `num_train_steps`.
-
-```py
-from tensorflow.keras.optimizers.schedules import PolynomialDecay
-
-batch_size = 8
-num_epochs = 3
-# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied
-# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset,
-# not the original Hugging Face Dataset, so its len() is already num_samples // batch_size.
-num_train_steps = len(tf_train_dataset) * num_epochs
-lr_scheduler = PolynomialDecay(
- initial_learning_rate=5e-5, end_learning_rate=0.0, decay_steps=num_train_steps
-)
-from tensorflow.keras.optimizers import Adam
-
-opt = Adam(learning_rate=lr_scheduler)
-```
-
-> [!TIP]
-> Die 🤗 Transformer Bibliothek hat eine `create_optimizer()`-Funktion, die einen `AdamW`-Optimierer mit Lernratenabfall erzeugt. Das ist eine praktisches Tool, auf das wir in den nächsten Abschnitten des Kurses im Detail eingehen werden.
-
-Somit haben wir einen neuen Optimierer definiert und können ihn zum Training verwenden. Zuerst laden wir das Modell neu, um die Änderungen an der Gewichtung aus dem letzten Trainingslauf zurückzusetzen, und dann können wir es mit dem neuen Optimierer kompilieren:
-
-```py
-import tensorflow as tf
-
-model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
-model.compile(optimizer=opt, loss=loss, metrics=["accuracy"])
-```
-
-Jetzt starten wir einen erneuten Trainingslauf mit `fit`:
-
-```py
-model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3)
-```
-
-> [!TIP]
-> 💡 Wenn du dein Modell während des Trainings automatisch in den Hub hochladen möchtest, kannst du in der Methode `model.fit()` einen `PushToHubCallback` mitgeben. Mehr darüber erfahren wir in [Kapitel 4](/course/chapter4/3)
-
-### Modell-Vorhersagen
-
-
-
-
-Trainieren und zusehen, wie der Verlust sinkt, ist ja ganz nett, aber was ist, wenn wir tatsächlich die Ergebnisse des trainierten Modells erhalten wollen? Entweder um Metriken zu berechnen oder um das Modell in der Produktion einzusetzen. Dafür können wir einfach die Methode `predict()` verwenden. Sie liefert uns die *Logits* aus dem Ausgabekopf des Modells, und zwar eine pro Klasse.
-
-```py
-preds = model.predict(tf_validation_dataset)["logits"]
-```
-
-Wir können diese Logits in die Klassenvorhersagen des Modells umwandeln, indem wir `argmax` verwenden, um den höchsten Logit zu finden, der der wahrscheinlichsten Klasse entspricht:
-
-```py
-class_preds = np.argmax(preds, axis=1)
-print(preds.shape, class_preds.shape)
-```
-
-```python out
-(408, 2) (408,)
-```
-
-Nun können wir diese Vorhersagen in `preds` nutzen, um einige Metriken zu berechnen! Wir können die Metriken, die mit dem MRPC-Datensatz verbunden sind, genauso einfach laden, wie wir den Datensatz geladen haben, in diesem Fall mit der Funktion "evaluate.load()". Das zurückgegebene Objekt verfügt über eine Berechnungsmethode, mit der wir die Metrik berechnen können:
-
-```py
-import evaluate
-
-metric = evaluate.load("glue", "mrpc")
-metric.compute(predictions=class_preds, references=raw_datasets["validation"]["label"])
-```
-
-```python out
-{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
-```
-Die genauen Ergebnisse können variieren, da die zufällige Initialisierung des Modellkopfes die errechneten Metriken verändern kann. Das Modell erreicht über den Validierungsdaten eine Genauigkeit von 85,78 % und ein F1-Maß von 89,97. Dies sind die beiden Kennzahlen, die zur Bewertung der Ergebnisse des MRPC-Datensatzes für das GLUE-Benchmark verwendet werden. In der Tabelle im [BERT-Paper] (https://arxiv.org/pdf/1810.04805.pdf) wird für das Basismodell ein F1-Maß von 88,9 angegeben. Dort wurde das `uncased` Modell verwendet, während wir hier das `cased` Modell verwenden, was das bessere Ergebnis erklärt.
-
-Damit ist die Einführung in das Fein-tunen mit der Keras-API abgeschlossen. Beispiele für die gängigsten CL-Aufgaben findest du in Kapitel 7.
-
-> [!TIP]
-> ✏️ **Probier es aus!** Fein-tune ein Modell mit dem GLUE SST-2 Datensatz, indem du die Datenverarbeitung aus Abschnitt 2 verwendest.
+
+
+# Modell mit Keras fein-tunen
+
+
+
+Wenn du die Datenvorverarbeitung im letzten Abschnitt abgeschlossen hast, brauchst es nur noch wenige Schritte, um das Modell zu trainieren. Beachte jedoch, dass der Befehl `model.fit()` auf einer CPU sehr langsam läuft. Wenn du keinen GPU hast, kannst du auf [Google Colab] (https://colab.research.google.com/) kostenlos auf GPUs und TPUs zugreifen.
+
+Bei den folgenden Codebeispielen wird davon ausgegangen, dass du die Beispiele aus dem vorherigen Abschnitt bereits ausgeführt hast. Hier ist eine kurze Zusammenfassung, die aufzeigt was erwartet wird:
+
+```py
+from datasets import load_dataset
+from transformers import AutoTokenizer, DataCollatorWithPadding
+import numpy as np
+
+raw_datasets = load_dataset("glue", "mrpc")
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+
+
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer, return_tensors="tf")
+
+tf_train_dataset = tokenized_datasets["train"].to_tf_dataset(
+ columns=["attention_mask", "input_ids", "token_type_ids"],
+ label_cols=["labels"],
+ shuffle=True,
+ collate_fn=data_collator,
+ batch_size=8,
+)
+
+tf_validation_dataset = tokenized_datasets["validation"].to_tf_dataset(
+ columns=["attention_mask", "input_ids", "token_type_ids"],
+ label_cols=["labels"],
+ shuffle=False,
+ collate_fn=data_collator,
+ batch_size=8,
+)
+```
+
+### Training
+
+Tensorflow Modelle, die von 🤗 Transformers importiert werden, sind bereits Keras Modelle. Hier ist eine kurze Einführung in Keras.
+
+
+
+Sobald wir die Daten haben, braucht es nur noch sehr wenig Arbeit, um mit dem Training zu beginnen.
+
+
+
+Wie im [vorherigen Kapitel](/course/chapter2) verwenden wir die Klasse `TFAutoModelForSequenceClassification` mit zwei Labels:
+
+```py
+from transformers import TFAutoModelForSequenceClassification
+
+model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+```
+
+Im Gegensatz zu [Kapitel 2](/course/chapter2) wird eine Warnung angezeigt, nachdem das Modell instanziiert wurde. Das liegt daran, dass BERT nicht auf die Klassifizierung von Satzpaaren vortrainiert wurde. Deshalb wurde der Kopf des vortrainierten Modells verworfen und stattdessen ein neuer Kopf eingefügt, der für die Klassifizierung von Sequenzen geeignet ist. Die Warnungen zeigen an, dass Teil der Gewichtung nicht verwendet wurden (die Gewichte für den verworfenen Kopf) und dass einige andere zufällig initialisiert wurden (die Gewichte für den neuen Kopf). Abschließend wirst du aufgefordert, das Modell zu trainieren, und genau das werden wir jetzt tun.
+
+Um das Modell mit unserem Datensatz fein-tunen zu können, müssen wir das Modell `kompilieren()` und unsere Daten an die `fit()`-Methode übergeben. Damit wird das Fein-tuning gestartet (dies sollte auf einer GPU ein paar Minuten dauern) und der Trainingsverlust sowie der Validierungsverlust am Ende jeder Epoche gemeldet.
+
+> [!TIP]
+> 🤗 Transformer Modelle haben eine besondere Fähigkeit, die die meisten Keras Modelle nicht haben - sie können automatisch einen geeigneten Verlust verwenden, der intern berechnet wird. Dieser Verlust wird standardmäßig verwendet, wenn in `compile()` kein Verlustargument angegeben wird. Um den internen Verlust zu verwenden, musst du deine Labels als Teil des Input übergeben und nicht als separates Label, wie es normalerweise bei Keras-Modellen der Fall ist. Beispiele dafür gibt es in Teil 2 des Kurses, wobei die Definition der richtigen Verlustfunktion schwierig sein kann. Für die Klassifizierung von Sequenzen eignet sich jedoch eine der Standardverlustfunktionen von Keras, die wir hier verwenden werden.
+
+```py
+from tensorflow.keras.losses import SparseCategoricalCrossentropy
+
+model.compile(
+ optimizer="adam",
+ loss=SparseCategoricalCrossentropy(from_logits=True),
+ metrics=["accuracy"],
+)
+model.fit(
+ tf_train_dataset,
+ validation_data=tf_validation_dataset,
+)
+```
+
+> [!WARNING]
+> Hier gibt es einen sehr häufigen Stolperstein - du *kannst* Keras einfach den Namen des Verlusts als String übergeben, aber standardmäßig geht Keras davon aus, dass du bereits einen Softmax auf die Outputs angewendet hast. Viele Modelle geben jedoch die Werte direkt vor der Anwendung des Softmax als *Logits* aus. Hier ist es wichtig der Keras Verlustfunktion mitzuteilen, dass unser Modell genau diess tut, und das geht nur indem sie direkt aufgerufen wird, und nicht über den Namen mit einem String.
+
+
+### Verbesserung der Trainingsperformance
+
+
+
+Wenn du den obigen Code ausprobierst, läuft er zwar, aber du wirst feststellen, dass der Verlust nur langsam oder sporadisch zurückgeht. Die Ursache hierfür ist die *Lernrate*. Wenn der Namen eines Optimierers als String an Keras übergeben wird, initialisiert Keras diesen Optimierer mit Standardwerten für alle Parameter, einschließlich der Lernrate. Aus langjähriger Erfahrung wissen wir, dass Transformer Modelle von einer wesentlich niedrigeren Lernrate profitieren als der Standardwert für Adam. Dieser Standardwert liegt bei 1e-3, auch geschrieben als 10 hoch -3 oder 0,001. Für Transformer ist 5e-5 (0,00005), was etwa zwanzigmal niedriger ist, ist ein viel besserer Ausgangspunkt.
+
+Zusätzlich zur Senkung der Lernrate haben wir noch einen zweiten Trick in petto: Wir können die Lernrate langsam im Laufe des Trainings verringern. In der Literatur wird dies manchmal als *Decay* oder *Annealing* der Lernrate bezeichnet. In Keras kannst das am besten mit dem *Lernraten-Scheduler* umgesetzt werden. Ein guter Scheduler ist `PolynomialDecay` - trotz des Namens lässt er die Lernrate in den Standardeinstellungen einfach linear vom Anfangswert bis zum Endwert abfallen. Dies ist genau was wir wollen. Um einen Scheduler richtig zu nutzen, müssen wir ihm allerdings sagen, wie lange das Training dauern soll. Das berechnen wir im Folgenden als `num_train_steps`.
+
+```py
+from tensorflow.keras.optimizers.schedules import PolynomialDecay
+
+batch_size = 8
+num_epochs = 3
+# The number of training steps is the number of samples in the dataset, divided by the batch size then multiplied
+# by the total number of epochs. Note that the tf_train_dataset here is a batched tf.data.Dataset,
+# not the original Hugging Face Dataset, so its len() is already num_samples // batch_size.
+num_train_steps = len(tf_train_dataset) * num_epochs
+lr_scheduler = PolynomialDecay(
+ initial_learning_rate=5e-5, end_learning_rate=0.0, decay_steps=num_train_steps
+)
+from tensorflow.keras.optimizers import Adam
+
+opt = Adam(learning_rate=lr_scheduler)
+```
+
+> [!TIP]
+> Die 🤗 Transformer Bibliothek hat eine `create_optimizer()`-Funktion, die einen `AdamW`-Optimierer mit Lernratenabfall erzeugt. Das ist eine praktisches Tool, auf das wir in den nächsten Abschnitten des Kurses im Detail eingehen werden.
+
+Somit haben wir einen neuen Optimierer definiert und können ihn zum Training verwenden. Zuerst laden wir das Modell neu, um die Änderungen an der Gewichtung aus dem letzten Trainingslauf zurückzusetzen, und dann können wir es mit dem neuen Optimierer kompilieren:
+
+```py
+import tensorflow as tf
+
+model = TFAutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+loss = tf.keras.losses.SparseCategoricalCrossentropy(from_logits=True)
+model.compile(optimizer=opt, loss=loss, metrics=["accuracy"])
+```
+
+Jetzt starten wir einen erneuten Trainingslauf mit `fit`:
+
+```py
+model.fit(tf_train_dataset, validation_data=tf_validation_dataset, epochs=3)
+```
+
+> [!TIP]
+> 💡 Wenn du dein Modell während des Trainings automatisch in den Hub hochladen möchtest, kannst du in der Methode `model.fit()` einen `PushToHubCallback` mitgeben. Mehr darüber erfahren wir in [Kapitel 4](/course/chapter4/3)
+
+### Modell-Vorhersagen
+
+
+
+
+Trainieren und zusehen, wie der Verlust sinkt, ist ja ganz nett, aber was ist, wenn wir tatsächlich die Ergebnisse des trainierten Modells erhalten wollen? Entweder um Metriken zu berechnen oder um das Modell in der Produktion einzusetzen. Dafür können wir einfach die Methode `predict()` verwenden. Sie liefert uns die *Logits* aus dem Ausgabekopf des Modells, und zwar eine pro Klasse.
+
+```py
+preds = model.predict(tf_validation_dataset)["logits"]
+```
+
+Wir können diese Logits in die Klassenvorhersagen des Modells umwandeln, indem wir `argmax` verwenden, um den höchsten Logit zu finden, der der wahrscheinlichsten Klasse entspricht:
+
+```py
+class_preds = np.argmax(preds, axis=1)
+print(preds.shape, class_preds.shape)
+```
+
+```python out
+(408, 2) (408,)
+```
+
+Nun können wir diese Vorhersagen in `preds` nutzen, um einige Metriken zu berechnen! Wir können die Metriken, die mit dem MRPC-Datensatz verbunden sind, genauso einfach laden, wie wir den Datensatz geladen haben, in diesem Fall mit der Funktion "evaluate.load()". Das zurückgegebene Objekt verfügt über eine Berechnungsmethode, mit der wir die Metrik berechnen können:
+
+```py
+import evaluate
+
+metric = evaluate.load("glue", "mrpc")
+metric.compute(predictions=class_preds, references=raw_datasets["validation"]["label"])
+```
+
+```python out
+{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
+```
+Die genauen Ergebnisse können variieren, da die zufällige Initialisierung des Modellkopfes die errechneten Metriken verändern kann. Das Modell erreicht über den Validierungsdaten eine Genauigkeit von 85,78 % und ein F1-Maß von 89,97. Dies sind die beiden Kennzahlen, die zur Bewertung der Ergebnisse des MRPC-Datensatzes für das GLUE-Benchmark verwendet werden. In der Tabelle im [BERT-Paper] (https://arxiv.org/pdf/1810.04805.pdf) wird für das Basismodell ein F1-Maß von 88,9 angegeben. Dort wurde das `uncased` Modell verwendet, während wir hier das `cased` Modell verwenden, was das bessere Ergebnis erklärt.
+
+Damit ist die Einführung in das Fein-tunen mit der Keras-API abgeschlossen. Beispiele für die gängigsten CL-Aufgaben findest du in Kapitel 7.
+
+> [!TIP]
+> ✏️ **Probier es aus!** Fein-tune ein Modell mit dem GLUE SST-2 Datensatz, indem du die Datenverarbeitung aus Abschnitt 2 verwendest.
diff --git a/chapters/de/chapter3/4.mdx b/chapters/de/chapter3/4.mdx
index 6395de2e0..cfcfff345 100644
--- a/chapters/de/chapter3/4.mdx
+++ b/chapters/de/chapter3/4.mdx
@@ -1,357 +1,357 @@
-# Komplettes Training
-
-
-
-
-
-In diesem Abschnitt befassen wir uns damit, wie wir die gleichen Ergebnisse wie im letzten Abschnitt erzielen können, ohne die Klasse `Trainer` zu verwenden. Auch hier gehen wir davon aus, dass du die Datenverarbeitung in Abschnitt 2 durchgeführt hast. Hier ist eine kurze Zusammenfassung mit allem, was du brauchst:
-
-```py
-from datasets import load_dataset
-from transformers import AutoTokenizer, DataCollatorWithPadding
-
-raw_datasets = load_dataset("glue", "mrpc")
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-
-def tokenize_function(example):
- return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
-
-
-tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
-```
-
-### Vorbereitung für das Training
-
-Bevor wir unsere Trainingsschleife schreiben, müssen wir noch einige Objekte definieren. Zunächst müssen wir die Datalader definieren, mit denen wir über die Batches iterieren werden. Doch bevor wir diese Dataloader definieren können, müssen wir unsere `tokenized_datasets` nachbearbeiten, um einige Dinge zu erledigen, die der `Trainer` automatisch für uns erledigt hat. Konkret heißt das, dass wir:
-
-- Die Spalten entfernen, die Werte enthalten, die das Modell nicht erwartet (wie die Spalten `sentence1` und `sentence2`).
-- Die Spalte `label` in `labels` umbenennen (weil das Modell erwartet, dass das Argument `labels` heißt).
-- Das Format der Datensätze anpassen, so dass sie PyTorch-Tensoren statt Listen zurückgeben.
-
-Das `tokenized_datasets` hat eine Methode für jeden dieser Schritte:
-
-```py
-tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
-tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
-tokenized_datasets.set_format("torch")
-tokenized_datasets["train"].column_names
-```
-
-Anschließend können wir überprüfen, ob der Output nur Spalten enthält, die unser Modell akzeptiert:
-
-```python
-["attention_mask", "input_ids", "labels", "token_type_ids"]
-```
-
-Jetzt können wir ganz einfach unsere Dataloader definieren:
-
-```py
-from torch.utils.data import DataLoader
-
-train_dataloader = DataLoader(
- tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
-)
-eval_dataloader = DataLoader(
- tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
-)
-```
-
-Um sicher zu gehen, überprüfen wir ein Batch auf Fehler in der Datenverarbeitung:
-
-```py
-for batch in train_dataloader:
- break
-{k: v.shape for k, v in batch.items()}
-```
-
-```python out
-{'attention_mask': torch.Size([8, 65]),
- 'input_ids': torch.Size([8, 65]),
- 'labels': torch.Size([8]),
- 'token_type_ids': torch.Size([8, 65])}
-```
-
-Beachte, dass die Dimensionen der Tensoren wahrscheinlich etwas anders aussehen werden, da wir für den Trainingsdatenlader `shuffle=True` eingestellt haben und innerhalb des Batches auf die maximale Länge auffüllen.
-
-Da wir nun mit der Datenvorverarbeitung fertig sind (ein zufriedenstellendes aber schwer erreichbares Ziel für jeden ML-Experten), können wir uns nun dem Modell zuwenden. Wir instanziieren es genauso wie im vorherigen Abschnitt:
-
-```py
-from transformers import AutoModelForSequenceClassification
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-```
-
-Als weitere Sicherheitsmaßnahme übergeben wir unseren Batch an das Modell, um sicherzustellen, dass beim Training alles glatt läuft:
-
-```py
-outputs = model(**batch)
-print(outputs.loss, outputs.logits.shape)
-```
-
-```python out
-tensor(0.5441, grad_fn=) torch.Size([8, 2])
-```
-
-Alle 🤗 Transformer Modelle geben den Verlust zurück, wenn `labels` angegeben werden, und wir erhalten zusätzlich die Logits (zwei für jede Eingabe in unserem Batch, also einen Tensor der Größe 8 x 2).
-
-Wir sind fast so weit, unsere Trainingsschleife zu schreiben! Es fehlen nur noch zwei Dinge: ein Optimierer und ein Scheduler für die Lernrate. Da wir versuchen, das zu wiederholen, was der `Trainer` automatisch gemacht hat, werden wir die gleichen Standardwerte verwenden. Der Optimierer, den der `Trainer` verwendet, heißt "AdamW" und ist größtenteils derselbe wie Adam, abgesehen von einer Abwandlung für die "Weight Decay Regularization" (siehe ["Decoupled Weight Decay Regularization"] (https://arxiv.org/abs/1711.05101) von Ilya Loshchilov und Frank Hutter):
-
-```py
-from torch.optim import AdamW
-
-optimizer = AdamW(model.parameters(), lr=5e-5)
-```
-
-Der standardmäßig verwendete Scheduler für die Lernrate ist ein linearer Abstieg vom Maximalwert (5e-5) auf 0. Um ihn richtig zu definieren, müssen wir die Anzahl der Trainingsschritte kennen, d.h. die Anzahl der Epochen, die die Trainingsschleife durchlaufen soll, multipliziert mit der Anzahl der Trainingsbatches (der Länge unseres Trainingsdatenordners). Der `Trainer` verwendet standardmäßig drei Epochen, woran wir uns hier orientieren werden:
-
-```py
-from transformers import get_scheduler
-
-num_epochs = 3
-num_training_steps = num_epochs * len(train_dataloader)
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-print(num_training_steps)
-```
-
-```python out
-1377
-```
-
-### Die Trainingsschleife
-
-Ein letzter Hinweis: Wir wollen die GPU zum Training nutzen, wenn wir Zugang zu einer haben (auf einer CPU kann das Training mehrere Stunden statt ein paar Minuten dauern). Dazu definieren wir `device` als Gerät auf dem wir unser Modell und unsere Batches speichern:
-
-```py
-import torch
-
-device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-model.to(device)
-device
-```
-
-```python out
-device(type='cuda')
-```
-
-Wir sind jetzt bereit für das Training! Um ein Gefühl dafür zu bekommen, wann das Training abgeschlossen sein wird, fügen wir mit der Bibliothek `tqdm` einen Fortschrittsbalken über die Anzahl der Trainingsschritte ein:
-
-```py
-from tqdm.auto import tqdm
-
-progress_bar = tqdm(range(num_training_steps))
-
-model.train()
-for epoch in range(num_epochs):
- for batch in train_dataloader:
- batch = {k: v.to(device) for k, v in batch.items()}
- outputs = model(**batch)
- loss = outputs.loss
- loss.backward()
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-Der Kern der Trainingsschleife sieht ähnlich aus wie in der Einleitung. Da wir keine Berichte angefordert haben, gibt die Trainingsschleife nichts über die Performance des Modells zurück. Dafür müssen wir eine Evaluationsschleife einfügen.
-
-### Die Evaluationsschleife
-
-Wie schon zuvor verwenden wir eine Metrik, die von der 🤗 Evaluate-Bibliothek bereitgestellt wird. Wir haben bereits die Methode `metric.compute()` gesehen, aber Metriken können auch Batches für uns akkumulieren, wenn wir die Vorhersageschleife mit der Methode `add_batch()` durchlaufen. Sobald wir alle Batches gesammelt haben, können wir das Endergebnis mit der Methode `metric.compute()` ermitteln. So implementierst du all das in eine Evaluationsschleife:
-
-```py
-import evaluate
-
-metric = evaluate.load("glue", "mrpc")
-model.eval()
-for batch in eval_dataloader:
- batch = {k: v.to(device) for k, v in batch.items()}
- with torch.no_grad():
- outputs = model(**batch)
-
- logits = outputs.logits
- predictions = torch.argmax(logits, dim=-1)
- metric.add_batch(predictions=predictions, references=batch["labels"])
-
-metric.compute()
-```
-
-```python out
-{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
-```
-
-Auch hier werden deine Ergebnisse wegen der Zufälligkeit bei der Initialisierung des Modellkopfes und der Datenverteilung etwas anders ausfallen, aber sie sollten in etwa gleich sein.
-
-> [!TIP]
-> ✏️ **Probier es selbt!** Ändere die vorherige Trainingsschleife, um dein Modell auf dem SST-2-Datensatz fein zu tunen.
-
-### Verbessere deine Trainingsschleife mit 🤗 Accelerate
-
-
-
-Die Trainingsschleife, die wir zuvor definiert haben, funktioniert gut auf einer einzelnen CPU oder GPU. Aber mit der Bibliothek [🤗 Accelerate](https://github.com/huggingface/accelerate) können wir mit wenigen Anpassungen verteiltes Training auf mehreren GPUs oder TPUs implementieren. Beginnend mit der Erstellung der Trainings- und Validierungsdaten, sieht unsere manuelle Trainingsschleife nun folgendermaßen aus:
-
-```py
-from torch.optim import AdamW
-from transformers import AutoModelForSequenceClassification, get_scheduler
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-optimizer = AdamW(model.parameters(), lr=3e-5)
-
-device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-model.to(device)
-
-num_epochs = 3
-num_training_steps = num_epochs * len(train_dataloader)
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-
-progress_bar = tqdm(range(num_training_steps))
-
-model.train()
-for epoch in range(num_epochs):
- for batch in train_dataloader:
- batch = {k: v.to(device) for k, v in batch.items()}
- outputs = model(**batch)
- loss = outputs.loss
- loss.backward()
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-Und hier sind die Änderungen:
-
-```diff
-+ from accelerate import Accelerator
- from torch.optim import AdamW
- from transformers import AutoModelForSequenceClassification, get_scheduler
-
-+ accelerator = Accelerator()
-
- model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
- optimizer = AdamW(model.parameters(), lr=3e-5)
-
-- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-- model.to(device)
-
-+ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare(
-+ train_dataloader, eval_dataloader, model, optimizer
-+ )
-
- num_epochs = 3
- num_training_steps = num_epochs * len(train_dataloader)
- lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps
- )
-
- progress_bar = tqdm(range(num_training_steps))
-
- model.train()
- for epoch in range(num_epochs):
- for batch in train_dataloader:
-- batch = {k: v.to(device) for k, v in batch.items()}
- outputs = model(**batch)
- loss = outputs.loss
-- loss.backward()
-+ accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-Die erste Zeile, die hinzugefügt werden muss, ist die Import-Zeile. Die zweite Zeile instanziiert ein `Accelerator`-Objekt, das die Hardware analysiert und die richtige verteilte Umgebung initialisiert. Accelerate kümmert sich um die Anordnung der Geräte, du kannst also die Zeilen entfernen, die das Modell auf dem Gerät platzieren (oder, wenn du das möchtest, sie so ändern, dass sie `accelerator.device` anstelle von `device` verwenden).
-
-Der Hauptteil der Arbeit wird dann in der Zeile erledigt, die die Dataloader, das Modell und den Optimierer an `accelerator.prepare()` sendet. Dadurch werden diese Objekte in den richtigen Container verpackt, damit das verteilte Training wie vorgesehen funktioniert. Die verbleibenden Änderungen sind das Entfernen der Zeile, die das Batch auf dem Gerät mit `device` ablegt (wenn du das beibehalten willst, kannst du es einfach in `accelerator.device` ändern) und das Ersetzen von `loss.backward()` durch `accelerator.backward(loss)`.
-
-> [!TIP]
-> ⚠️ Um von dem Geschwindigkeitsvorteil der Cloud TPUs zu profitieren, empfehlen wir, deine Samples mit den Argumenten `padding="max_length"` und `max_length` des Tokenizers auf eine feste Länge aufzufüllen.
-
-Wenn du damit experimentieren möchtest, siehst du hier, wie die komplette Trainingsschleife mit 🤗 Accelerate aussieht:
-
-```py
-from accelerate import Accelerator
-from torch.optim import AdamW
-from transformers import AutoModelForSequenceClassification, get_scheduler
-
-accelerator = Accelerator()
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-optimizer = AdamW(model.parameters(), lr=3e-5)
-
-train_dl, eval_dl, model, optimizer = accelerator.prepare(
- train_dataloader, eval_dataloader, model, optimizer
-)
-
-num_epochs = 3
-num_training_steps = num_epochs * len(train_dl)
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-
-progress_bar = tqdm(range(num_training_steps))
-
-model.train()
-for epoch in range(num_epochs):
- for batch in train_dl:
- outputs = model(**batch)
- loss = outputs.loss
- accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-Wenn dies in das Script `train.py` eingefügt wird, kann das Script auf jeder Art von verteilter Hardware ausgeführt werden. Um es auf deiner verteilten Hardware auszuprobieren, führe den folgenden Befehl aus:
-
-```bash
-accelerate config
-```
-
-Du wirst dann aufgefordert werden, einige Fragen zu beantworten und die Antworten in eine Konfigurationsdatei zu schreiben, die von diesem Befehl verwendet wird:
-
-```
-accelerate launch train.py
-```
-
-Damit wird das verteilte Training gestartet.
-
-Wenn du das in einem Notebook ausprobieren möchtest (z. B. um es mit TPUs auf Colab zu testen), füge den Code einfach in eine `training_function()` ein und führe eine letzte Zelle mit aus:
-
-```python
-from accelerate import notebook_launcher
-
-notebook_launcher(training_function)
-```
-
-Weitere Beispiele findest du in dem [🤗 Accelerate Repo](https://github.com/huggingface/accelerate/tree/main/examples).
+# Komplettes Training
+
+
+
+
+
+In diesem Abschnitt befassen wir uns damit, wie wir die gleichen Ergebnisse wie im letzten Abschnitt erzielen können, ohne die Klasse `Trainer` zu verwenden. Auch hier gehen wir davon aus, dass du die Datenverarbeitung in Abschnitt 2 durchgeführt hast. Hier ist eine kurze Zusammenfassung mit allem, was du brauchst:
+
+```py
+from datasets import load_dataset
+from transformers import AutoTokenizer, DataCollatorWithPadding
+
+raw_datasets = load_dataset("glue", "mrpc")
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+
+
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+
+### Vorbereitung für das Training
+
+Bevor wir unsere Trainingsschleife schreiben, müssen wir noch einige Objekte definieren. Zunächst müssen wir die Datalader definieren, mit denen wir über die Batches iterieren werden. Doch bevor wir diese Dataloader definieren können, müssen wir unsere `tokenized_datasets` nachbearbeiten, um einige Dinge zu erledigen, die der `Trainer` automatisch für uns erledigt hat. Konkret heißt das, dass wir:
+
+- Die Spalten entfernen, die Werte enthalten, die das Modell nicht erwartet (wie die Spalten `sentence1` und `sentence2`).
+- Die Spalte `label` in `labels` umbenennen (weil das Modell erwartet, dass das Argument `labels` heißt).
+- Das Format der Datensätze anpassen, so dass sie PyTorch-Tensoren statt Listen zurückgeben.
+
+Das `tokenized_datasets` hat eine Methode für jeden dieser Schritte:
+
+```py
+tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
+tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
+tokenized_datasets.set_format("torch")
+tokenized_datasets["train"].column_names
+```
+
+Anschließend können wir überprüfen, ob der Output nur Spalten enthält, die unser Modell akzeptiert:
+
+```python
+["attention_mask", "input_ids", "labels", "token_type_ids"]
+```
+
+Jetzt können wir ganz einfach unsere Dataloader definieren:
+
+```py
+from torch.utils.data import DataLoader
+
+train_dataloader = DataLoader(
+ tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
+)
+eval_dataloader = DataLoader(
+ tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
+)
+```
+
+Um sicher zu gehen, überprüfen wir ein Batch auf Fehler in der Datenverarbeitung:
+
+```py
+for batch in train_dataloader:
+ break
+{k: v.shape for k, v in batch.items()}
+```
+
+```python out
+{'attention_mask': torch.Size([8, 65]),
+ 'input_ids': torch.Size([8, 65]),
+ 'labels': torch.Size([8]),
+ 'token_type_ids': torch.Size([8, 65])}
+```
+
+Beachte, dass die Dimensionen der Tensoren wahrscheinlich etwas anders aussehen werden, da wir für den Trainingsdatenlader `shuffle=True` eingestellt haben und innerhalb des Batches auf die maximale Länge auffüllen.
+
+Da wir nun mit der Datenvorverarbeitung fertig sind (ein zufriedenstellendes aber schwer erreichbares Ziel für jeden ML-Experten), können wir uns nun dem Modell zuwenden. Wir instanziieren es genauso wie im vorherigen Abschnitt:
+
+```py
+from transformers import AutoModelForSequenceClassification
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+```
+
+Als weitere Sicherheitsmaßnahme übergeben wir unseren Batch an das Modell, um sicherzustellen, dass beim Training alles glatt läuft:
+
+```py
+outputs = model(**batch)
+print(outputs.loss, outputs.logits.shape)
+```
+
+```python out
+tensor(0.5441, grad_fn=) torch.Size([8, 2])
+```
+
+Alle 🤗 Transformer Modelle geben den Verlust zurück, wenn `labels` angegeben werden, und wir erhalten zusätzlich die Logits (zwei für jede Eingabe in unserem Batch, also einen Tensor der Größe 8 x 2).
+
+Wir sind fast so weit, unsere Trainingsschleife zu schreiben! Es fehlen nur noch zwei Dinge: ein Optimierer und ein Scheduler für die Lernrate. Da wir versuchen, das zu wiederholen, was der `Trainer` automatisch gemacht hat, werden wir die gleichen Standardwerte verwenden. Der Optimierer, den der `Trainer` verwendet, heißt "AdamW" und ist größtenteils derselbe wie Adam, abgesehen von einer Abwandlung für die "Weight Decay Regularization" (siehe ["Decoupled Weight Decay Regularization"] (https://arxiv.org/abs/1711.05101) von Ilya Loshchilov und Frank Hutter):
+
+```py
+from torch.optim import AdamW
+
+optimizer = AdamW(model.parameters(), lr=5e-5)
+```
+
+Der standardmäßig verwendete Scheduler für die Lernrate ist ein linearer Abstieg vom Maximalwert (5e-5) auf 0. Um ihn richtig zu definieren, müssen wir die Anzahl der Trainingsschritte kennen, d.h. die Anzahl der Epochen, die die Trainingsschleife durchlaufen soll, multipliziert mit der Anzahl der Trainingsbatches (der Länge unseres Trainingsdatenordners). Der `Trainer` verwendet standardmäßig drei Epochen, woran wir uns hier orientieren werden:
+
+```py
+from transformers import get_scheduler
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dataloader)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+print(num_training_steps)
+```
+
+```python out
+1377
+```
+
+### Die Trainingsschleife
+
+Ein letzter Hinweis: Wir wollen die GPU zum Training nutzen, wenn wir Zugang zu einer haben (auf einer CPU kann das Training mehrere Stunden statt ein paar Minuten dauern). Dazu definieren wir `device` als Gerät auf dem wir unser Modell und unsere Batches speichern:
+
+```py
+import torch
+
+device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+model.to(device)
+device
+```
+
+```python out
+device(type='cuda')
+```
+
+Wir sind jetzt bereit für das Training! Um ein Gefühl dafür zu bekommen, wann das Training abgeschlossen sein wird, fügen wir mit der Bibliothek `tqdm` einen Fortschrittsbalken über die Anzahl der Trainingsschritte ein:
+
+```py
+from tqdm.auto import tqdm
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ outputs = model(**batch)
+ loss = outputs.loss
+ loss.backward()
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+Der Kern der Trainingsschleife sieht ähnlich aus wie in der Einleitung. Da wir keine Berichte angefordert haben, gibt die Trainingsschleife nichts über die Performance des Modells zurück. Dafür müssen wir eine Evaluationsschleife einfügen.
+
+### Die Evaluationsschleife
+
+Wie schon zuvor verwenden wir eine Metrik, die von der 🤗 Evaluate-Bibliothek bereitgestellt wird. Wir haben bereits die Methode `metric.compute()` gesehen, aber Metriken können auch Batches für uns akkumulieren, wenn wir die Vorhersageschleife mit der Methode `add_batch()` durchlaufen. Sobald wir alle Batches gesammelt haben, können wir das Endergebnis mit der Methode `metric.compute()` ermitteln. So implementierst du all das in eine Evaluationsschleife:
+
+```py
+import evaluate
+
+metric = evaluate.load("glue", "mrpc")
+model.eval()
+for batch in eval_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ with torch.no_grad():
+ outputs = model(**batch)
+
+ logits = outputs.logits
+ predictions = torch.argmax(logits, dim=-1)
+ metric.add_batch(predictions=predictions, references=batch["labels"])
+
+metric.compute()
+```
+
+```python out
+{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
+```
+
+Auch hier werden deine Ergebnisse wegen der Zufälligkeit bei der Initialisierung des Modellkopfes und der Datenverteilung etwas anders ausfallen, aber sie sollten in etwa gleich sein.
+
+> [!TIP]
+> ✏️ **Probier es selbt!** Ändere die vorherige Trainingsschleife, um dein Modell auf dem SST-2-Datensatz fein zu tunen.
+
+### Verbessere deine Trainingsschleife mit 🤗 Accelerate
+
+
+
+Die Trainingsschleife, die wir zuvor definiert haben, funktioniert gut auf einer einzelnen CPU oder GPU. Aber mit der Bibliothek [🤗 Accelerate](https://github.com/huggingface/accelerate) können wir mit wenigen Anpassungen verteiltes Training auf mehreren GPUs oder TPUs implementieren. Beginnend mit der Erstellung der Trainings- und Validierungsdaten, sieht unsere manuelle Trainingsschleife nun folgendermaßen aus:
+
+```py
+from torch.optim import AdamW
+from transformers import AutoModelForSequenceClassification, get_scheduler
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+optimizer = AdamW(model.parameters(), lr=3e-5)
+
+device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+model.to(device)
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dataloader)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ outputs = model(**batch)
+ loss = outputs.loss
+ loss.backward()
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+Und hier sind die Änderungen:
+
+```diff
++ from accelerate import Accelerator
+ from torch.optim import AdamW
+ from transformers import AutoModelForSequenceClassification, get_scheduler
+
++ accelerator = Accelerator()
+
+ model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+ optimizer = AdamW(model.parameters(), lr=3e-5)
+
+- device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+- model.to(device)
+
++ train_dataloader, eval_dataloader, model, optimizer = accelerator.prepare(
++ train_dataloader, eval_dataloader, model, optimizer
++ )
+
+ num_epochs = 3
+ num_training_steps = num_epochs * len(train_dataloader)
+ lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps
+ )
+
+ progress_bar = tqdm(range(num_training_steps))
+
+ model.train()
+ for epoch in range(num_epochs):
+ for batch in train_dataloader:
+- batch = {k: v.to(device) for k, v in batch.items()}
+ outputs = model(**batch)
+ loss = outputs.loss
+- loss.backward()
++ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+Die erste Zeile, die hinzugefügt werden muss, ist die Import-Zeile. Die zweite Zeile instanziiert ein `Accelerator`-Objekt, das die Hardware analysiert und die richtige verteilte Umgebung initialisiert. Accelerate kümmert sich um die Anordnung der Geräte, du kannst also die Zeilen entfernen, die das Modell auf dem Gerät platzieren (oder, wenn du das möchtest, sie so ändern, dass sie `accelerator.device` anstelle von `device` verwenden).
+
+Der Hauptteil der Arbeit wird dann in der Zeile erledigt, die die Dataloader, das Modell und den Optimierer an `accelerator.prepare()` sendet. Dadurch werden diese Objekte in den richtigen Container verpackt, damit das verteilte Training wie vorgesehen funktioniert. Die verbleibenden Änderungen sind das Entfernen der Zeile, die das Batch auf dem Gerät mit `device` ablegt (wenn du das beibehalten willst, kannst du es einfach in `accelerator.device` ändern) und das Ersetzen von `loss.backward()` durch `accelerator.backward(loss)`.
+
+> [!TIP]
+> ⚠️ Um von dem Geschwindigkeitsvorteil der Cloud TPUs zu profitieren, empfehlen wir, deine Samples mit den Argumenten `padding="max_length"` und `max_length` des Tokenizers auf eine feste Länge aufzufüllen.
+
+Wenn du damit experimentieren möchtest, siehst du hier, wie die komplette Trainingsschleife mit 🤗 Accelerate aussieht:
+
+```py
+from accelerate import Accelerator
+from torch.optim import AdamW
+from transformers import AutoModelForSequenceClassification, get_scheduler
+
+accelerator = Accelerator()
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+optimizer = AdamW(model.parameters(), lr=3e-5)
+
+train_dl, eval_dl, model, optimizer = accelerator.prepare(
+ train_dataloader, eval_dataloader, model, optimizer
+)
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dl)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dl:
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+Wenn dies in das Script `train.py` eingefügt wird, kann das Script auf jeder Art von verteilter Hardware ausgeführt werden. Um es auf deiner verteilten Hardware auszuprobieren, führe den folgenden Befehl aus:
+
+```bash
+accelerate config
+```
+
+Du wirst dann aufgefordert werden, einige Fragen zu beantworten und die Antworten in eine Konfigurationsdatei zu schreiben, die von diesem Befehl verwendet wird:
+
+```
+accelerate launch train.py
+```
+
+Damit wird das verteilte Training gestartet.
+
+Wenn du das in einem Notebook ausprobieren möchtest (z. B. um es mit TPUs auf Colab zu testen), füge den Code einfach in eine `training_function()` ein und führe eine letzte Zelle mit aus:
+
+```python
+from accelerate import notebook_launcher
+
+notebook_launcher(training_function)
+```
+
+Weitere Beispiele findest du in dem [🤗 Accelerate Repo](https://github.com/huggingface/accelerate/tree/main/examples).
diff --git a/chapters/de/chapter3/5.mdx b/chapters/de/chapter3/5.mdx
index a017a1189..c585f17fa 100644
--- a/chapters/de/chapter3/5.mdx
+++ b/chapters/de/chapter3/5.mdx
@@ -1,25 +1,25 @@
-
-
-# Fein-tunen, Check!
-
-
-
-Das hat Spaß gemacht! In den ersten beiden Kapiteln hast du etwas über Modelle und Tokenizer gelernt, und jetzt weißt du, wie du sie auf deine eigenen Daten fein-tunen kannst. Rekapitulieren wir, was du in diesem Kapitel gelernt hast:
-
-{#if fw === 'pt'}
-* Über Datensätze im [Hub](https://huggingface.co/datasets)
-* Wie du Datensätze lädst und vorverarbeitest, einschließlich der Verwendung von dynamischem Padding und Collators
-* Implementierung des eigenen Fein-tunings und der Evaluierung eines Modells
-* Eine Trainingsschleife auf niedriger Ebene implementiert
-* Mit 🤗 Accelerate die Trainingsschleife so anpassen, dass sie für mehrere GPUs oder TPUs funktioniert
-
-{:else}
-* Über Datensätze im [Hub](https://huggingface.co/datasets)
-* Wie man Datensätze lädt und vorverarbeitet
-* Wie man ein Modell mit Keras fein-tunet und auswertet
-* Eine eigene Metrik implementiert
-
-{/if}
+
+
+# Fein-tunen, Check!
+
+
+
+Das hat Spaß gemacht! In den ersten beiden Kapiteln hast du etwas über Modelle und Tokenizer gelernt, und jetzt weißt du, wie du sie auf deine eigenen Daten fein-tunen kannst. Rekapitulieren wir, was du in diesem Kapitel gelernt hast:
+
+{#if fw === 'pt'}
+* Über Datensätze im [Hub](https://huggingface.co/datasets)
+* Wie du Datensätze lädst und vorverarbeitest, einschließlich der Verwendung von dynamischem Padding und Collators
+* Implementierung des eigenen Fein-tunings und der Evaluierung eines Modells
+* Eine Trainingsschleife auf niedriger Ebene implementiert
+* Mit 🤗 Accelerate die Trainingsschleife so anpassen, dass sie für mehrere GPUs oder TPUs funktioniert
+
+{:else}
+* Über Datensätze im [Hub](https://huggingface.co/datasets)
+* Wie man Datensätze lädt und vorverarbeitet
+* Wie man ein Modell mit Keras fein-tunet und auswertet
+* Eine eigene Metrik implementiert
+
+{/if}
diff --git a/chapters/de/chapter3/6.mdx b/chapters/de/chapter3/6.mdx
index cd37e7f5c..fef9f12c8 100644
--- a/chapters/de/chapter3/6.mdx
+++ b/chapters/de/chapter3/6.mdx
@@ -1,301 +1,301 @@
-
-
-
-
-# Quiz am Ende des Kapitels
-
-
-
-Teste, was du in diesem Kapitel gelernt hast!
-
-### 1. Der Datensatz `emotion` enthält Twitter-Nachrichten, die mit Emotionen gelabelt sind. Suche im [Hub](https://huggingface.co/datasets) nach dem Datensatz und lies die Datensatzkarte. Welche der folgenden Emotionen gehört nicht zu den grundlegenden Emotionen?
-
-
-
-### 2. Suche im [Hub](https://huggingface.co/datasets) nach dem Datensatz `ar_sarcasm`. Welche Aufgabe unterstützt er?
-
-Datensatzkarte an!"
- },
- {
- text: "Named entity recognition (Eigennamenerkennung )",
- explain: "Das war's noch nicht - schau dir noch mal die Datensatzkarte an!"
- },
- {
- text: "Question answering (Fragenbeantwortung)",
- explain: "Leider wurde diese Frage nicht richtig beantwortet. Versuche es noch einmal!"
- }
- ]}
-/>
-
-### 3. Wie erwartet das BERT Modell, dass ein Satzpaar verarbeitet wird?
-
-[SEP] spezielles Token wird benötigt, um die beiden Sätze zu trennen, aber das ist nicht das Einzige!"
- },
- {
- text: "[CLS] Satztoken_1 Satztoken_2",
- explain: "Ein [CLS] spezielles Token ist am Anfang erforderlich, aber das ist nicht das Einzige!"
- },
- {
- text: "[CLS] Satztoken_1 [SEP] Satztoken_2 [SEP]",
- explain: "Das ist richtig!",
- correct: true
- },
- {
- text: "[CLS] Satztoken_1 [SEP] Satztoken_2",
- explain: "Ein [CLS] spezielles Token wird am Anfang benötigt, sowie ein [SEP] spezielles Token, um die beiden Sätze zu trennen, aber das ist noch nicht alles!"
- }
- ]}
-/>
-
-{#if fw === 'pt'}
-### 4. Was sind die Vorteile der Methode `Dataset.map()`?
-
-
-
-### 5. Was bedeutet dynamisches Padding?
-
-
-
-### 6. Welchen Zweck hat die Funktion collate?
-
-DataCollatorWithPadding im Speziellen."
- },
- {
- text: "Er stellt alle Proben in einem Batch zusammen.",
- explain: "Richtig! Du kannst die collate Funktion als Argument eines DataLoaders übergeben. Wir haben die Funktion DataCollatorWithPadding verwendet, die alle Elemente in einem Batch auffüllt, damit sie die gleiche Länge haben.",
- correct: true
- },
- {
- text: "Es wird der gesamte Datensatz vorverarbeitet.",
- explain: "Das wäre eine Vorverarbeitungsfunktion, keine Zusammenfassungsfunktion wie collate."
- },
- {
- text: "Sie schneidet die Sequenzen im Datensatz ab.",
- explain: "Eine collate Funktion behandelt einzelne Batches, nicht den gesamten Datensatz. Wenn du am Abschneiden interessiert bist, kannst du das truncate Argument von dem tokenizer verwenden."
- }
- ]}
-/>
-
-### 7. Was passiert, wenn du eine der Klassen `AutoModelForXxx` mit einem vortrainierten Sprachmodell (z. B. `bert-base-uncased`) instanziierst, das einer anderen Aufgabe entspricht als der, für die es trainiert wurde?
-
-AutoModelForSequenceClassification mit bert-base-uncased verwendet haben, bekamen wir beim Instanziieren des Modells Warnungen. Der trainierte Kopf wird nicht für die Sequenzklassifizierung verwendet, also wird er verworfen und ein neuer Kopf mit zufälliger Gewichtung wird instanziiert.",
- correct: true
- },
- {
- text: "Der Kopf des vorher trainierten Modells wird verworfen.",
- explain: "Es muss etwas anderes passieren. Versuch es noch einmal!"
- },
- {
- text: "Nichts, da das Modell noch für die andere Aufgabe fein-tunen kann.",
- explain: "Der Kopf des trainierten Modells wurde nicht für die Lösung dieser Aufgabe trainiert, also sollte der Kopf verworfen werden!"
- }
- ]}
-/>
-
-### 8. Was ist der Zweck von `TrainingArguments`?
-
-Trainer verwendet werden.",
- explain: "Richtig!",
- correct: true
- },
- {
- text: "Hier wird die Größe des Modells angegeben.",
- explain: "Die Größe des Modells wird durch die Konfiguration des Modells festgelegt, nicht durch die Klasse TrainingArguments."
- },
- {
- text: "Sie enthält nur die Hyperparameter, die für die Auswertung verwendet werden.",
- explain: "In unserem Beispiel haben wir angegeben, wo das Modell und seine Kontrollpunkte gespeichert werden sollen. Versuche es noch einmal!"
- },
- {
- text: "Er enthält nur die Hyperparameter, die für das Training verwendet werden.",
- explain: "In unserem Beispiel haben wir auch eine Auswertungsstrategie verwendet, was sich auf die Auswertung auswirkt. Versuche es noch einmal!"
- }
- ]}
-/>
-
-### 9. Warum solltest du die 🤗 Accelerate Bibliothek benutzen?
-
-Trainer gemacht, nicht mit der 🤗 Accelerate Bibliothek. Versuch es noch einmal!"
- },
- {
- text: "Damit funktionieren unsere Trainingsschleifen bei verteilten Strategien.",
- explain: "Richtig! Mit 🤗 Accelerate funktionieren deine Trainingsschleifen für mehrere verteilte GPUs und TPUs.",
- correct: true
- },
- {
- text: "Es bietet mehr Funktionen zur Optimierung.",
- explain: "Nein, die 🤗 Accelerate Bibliothek stellt keine Optimierungsfunktionen zur Verfügung."
- }
- ]}
-/>
-
-{:else}
-### 4. Was passiert, wenn du eine der Klassen `TFAutoModelForXxx` mit einem vortrainierten Sprachmodell (z. B. `bert-base-uncased`) instanziierst, das einer anderen Aufgabe entspricht als der, für die es trainiert wurde?
-
-TFAutoModelForSequenceClassification mit bert-base-uncased verwendet haben, bekamen wir beim Instanziieren des Modells Warnungen. Der trainierte Kopf wird nicht für die Sequenzklassifizierung verwendet, also wird er verworfen und ein neuer Kopf mit zufälligen Gewichten instanziiert.",
- correct: true
- },
- {
- text: "Der Kopf des vorher trainierten Modells wird verworfen.",
- explain: "Es muss etwas anderes passieren. Versuch es noch einmal!"
- },
- {
- text: "Nichts, da das Modell noch für die andere Aufgabe fein-tunen kann.",
- explain: "Der Kopf des trainierten Modells wurde nicht für die Lösung dieser Aufgabe trainiert, also sollten wir den Kopf verwerfen!"
- }
- ]}
-/>
-
-### 5. Die Tensorflow Modelle von `transformers` sind bereits Keras Modelle. Welchen Vorteil bietet das?
-
-TPUStrategy-Bereich ausführen, einschließlich der Initialisierung des Modells."
- },
- {
- text: "Du kannst bestehende Methoden wie compile(), fit() und predict() nutzen.",
- erklären: "Richtig! Wenn du erst einmal die Daten hast, erfordert das Training mit ihnen nur noch wenig Arbeit.",
- correct: true
- },
- {
- text: "Du lernst sowohl Keras als auch Transformer kennen.",
- explain: "Korrekt, aber wir suchen nach etwas anderem :)",
- correct: true
- },
- {
- text: "Du kannst ganz einfach Metriken für den Datensatz berechnen.",
- explain: "Keras hilft uns beim Training und der Auswertung des Modells, nicht bei der Berechnung von datensatzbezogenen Metriken."
- }
- ]}
-/>
-
-### 6. Wie kannst du deine eigene benutzerdefinierte Metrik definieren?
-
-tf.keras.metrics.Metric erstellen.",
- explain: "Großartig!",
- correct: true
- },
- {
- text: "Durch Verwendung der funktionalen Keras-API.",
- explain: "Versuch es noch einmal!"
- },
- {
- text: "Durch die Verwendung einer Callable mit der Signatur metric_fn(y_true, y_pred).",
- explain: "Korrekt!",
- correct: true
- },
- {
- text: "Indem du es googelst.",
- explain: "Das ist nicht die Antwort, nach der wir suchen, aber es sollte dir helfen, sie zu finden.",
- correct: true
- }
- ]}
-/>
-
-{/if}
+
+
+
+
+# Quiz am Ende des Kapitels
+
+
+
+Teste, was du in diesem Kapitel gelernt hast!
+
+### 1. Der Datensatz `emotion` enthält Twitter-Nachrichten, die mit Emotionen gelabelt sind. Suche im [Hub](https://huggingface.co/datasets) nach dem Datensatz und lies die Datensatzkarte. Welche der folgenden Emotionen gehört nicht zu den grundlegenden Emotionen?
+
+
+
+### 2. Suche im [Hub](https://huggingface.co/datasets) nach dem Datensatz `ar_sarcasm`. Welche Aufgabe unterstützt er?
+
+Datensatzkarte an!"
+ },
+ {
+ text: "Named entity recognition (Eigennamenerkennung )",
+ explain: "Das war's noch nicht - schau dir noch mal die Datensatzkarte an!"
+ },
+ {
+ text: "Question answering (Fragenbeantwortung)",
+ explain: "Leider wurde diese Frage nicht richtig beantwortet. Versuche es noch einmal!"
+ }
+ ]}
+/>
+
+### 3. Wie erwartet das BERT Modell, dass ein Satzpaar verarbeitet wird?
+
+[SEP] spezielles Token wird benötigt, um die beiden Sätze zu trennen, aber das ist nicht das Einzige!"
+ },
+ {
+ text: "[CLS] Satztoken_1 Satztoken_2",
+ explain: "Ein [CLS] spezielles Token ist am Anfang erforderlich, aber das ist nicht das Einzige!"
+ },
+ {
+ text: "[CLS] Satztoken_1 [SEP] Satztoken_2 [SEP]",
+ explain: "Das ist richtig!",
+ correct: true
+ },
+ {
+ text: "[CLS] Satztoken_1 [SEP] Satztoken_2",
+ explain: "Ein [CLS] spezielles Token wird am Anfang benötigt, sowie ein [SEP] spezielles Token, um die beiden Sätze zu trennen, aber das ist noch nicht alles!"
+ }
+ ]}
+/>
+
+{#if fw === 'pt'}
+### 4. Was sind die Vorteile der Methode `Dataset.map()`?
+
+
+
+### 5. Was bedeutet dynamisches Padding?
+
+
+
+### 6. Welchen Zweck hat die Funktion collate?
+
+DataCollatorWithPadding im Speziellen."
+ },
+ {
+ text: "Er stellt alle Proben in einem Batch zusammen.",
+ explain: "Richtig! Du kannst die collate Funktion als Argument eines DataLoaders übergeben. Wir haben die Funktion DataCollatorWithPadding verwendet, die alle Elemente in einem Batch auffüllt, damit sie die gleiche Länge haben.",
+ correct: true
+ },
+ {
+ text: "Es wird der gesamte Datensatz vorverarbeitet.",
+ explain: "Das wäre eine Vorverarbeitungsfunktion, keine Zusammenfassungsfunktion wie collate."
+ },
+ {
+ text: "Sie schneidet die Sequenzen im Datensatz ab.",
+ explain: "Eine collate Funktion behandelt einzelne Batches, nicht den gesamten Datensatz. Wenn du am Abschneiden interessiert bist, kannst du das truncate Argument von dem tokenizer verwenden."
+ }
+ ]}
+/>
+
+### 7. Was passiert, wenn du eine der Klassen `AutoModelForXxx` mit einem vortrainierten Sprachmodell (z. B. `bert-base-uncased`) instanziierst, das einer anderen Aufgabe entspricht als der, für die es trainiert wurde?
+
+AutoModelForSequenceClassification mit bert-base-uncased verwendet haben, bekamen wir beim Instanziieren des Modells Warnungen. Der trainierte Kopf wird nicht für die Sequenzklassifizierung verwendet, also wird er verworfen und ein neuer Kopf mit zufälliger Gewichtung wird instanziiert.",
+ correct: true
+ },
+ {
+ text: "Der Kopf des vorher trainierten Modells wird verworfen.",
+ explain: "Es muss etwas anderes passieren. Versuch es noch einmal!"
+ },
+ {
+ text: "Nichts, da das Modell noch für die andere Aufgabe fein-tunen kann.",
+ explain: "Der Kopf des trainierten Modells wurde nicht für die Lösung dieser Aufgabe trainiert, also sollte der Kopf verworfen werden!"
+ }
+ ]}
+/>
+
+### 8. Was ist der Zweck von `TrainingArguments`?
+
+Trainer verwendet werden.",
+ explain: "Richtig!",
+ correct: true
+ },
+ {
+ text: "Hier wird die Größe des Modells angegeben.",
+ explain: "Die Größe des Modells wird durch die Konfiguration des Modells festgelegt, nicht durch die Klasse TrainingArguments."
+ },
+ {
+ text: "Sie enthält nur die Hyperparameter, die für die Auswertung verwendet werden.",
+ explain: "In unserem Beispiel haben wir angegeben, wo das Modell und seine Kontrollpunkte gespeichert werden sollen. Versuche es noch einmal!"
+ },
+ {
+ text: "Er enthält nur die Hyperparameter, die für das Training verwendet werden.",
+ explain: "In unserem Beispiel haben wir auch eine Auswertungsstrategie verwendet, was sich auf die Auswertung auswirkt. Versuche es noch einmal!"
+ }
+ ]}
+/>
+
+### 9. Warum solltest du die 🤗 Accelerate Bibliothek benutzen?
+
+Trainer gemacht, nicht mit der 🤗 Accelerate Bibliothek. Versuch es noch einmal!"
+ },
+ {
+ text: "Damit funktionieren unsere Trainingsschleifen bei verteilten Strategien.",
+ explain: "Richtig! Mit 🤗 Accelerate funktionieren deine Trainingsschleifen für mehrere verteilte GPUs und TPUs.",
+ correct: true
+ },
+ {
+ text: "Es bietet mehr Funktionen zur Optimierung.",
+ explain: "Nein, die 🤗 Accelerate Bibliothek stellt keine Optimierungsfunktionen zur Verfügung."
+ }
+ ]}
+/>
+
+{:else}
+### 4. Was passiert, wenn du eine der Klassen `TFAutoModelForXxx` mit einem vortrainierten Sprachmodell (z. B. `bert-base-uncased`) instanziierst, das einer anderen Aufgabe entspricht als der, für die es trainiert wurde?
+
+TFAutoModelForSequenceClassification mit bert-base-uncased verwendet haben, bekamen wir beim Instanziieren des Modells Warnungen. Der trainierte Kopf wird nicht für die Sequenzklassifizierung verwendet, also wird er verworfen und ein neuer Kopf mit zufälligen Gewichten instanziiert.",
+ correct: true
+ },
+ {
+ text: "Der Kopf des vorher trainierten Modells wird verworfen.",
+ explain: "Es muss etwas anderes passieren. Versuch es noch einmal!"
+ },
+ {
+ text: "Nichts, da das Modell noch für die andere Aufgabe fein-tunen kann.",
+ explain: "Der Kopf des trainierten Modells wurde nicht für die Lösung dieser Aufgabe trainiert, also sollten wir den Kopf verwerfen!"
+ }
+ ]}
+/>
+
+### 5. Die Tensorflow Modelle von `transformers` sind bereits Keras Modelle. Welchen Vorteil bietet das?
+
+TPUStrategy-Bereich ausführen, einschließlich der Initialisierung des Modells."
+ },
+ {
+ text: "Du kannst bestehende Methoden wie compile(), fit() und predict() nutzen.",
+ erklären: "Richtig! Wenn du erst einmal die Daten hast, erfordert das Training mit ihnen nur noch wenig Arbeit.",
+ correct: true
+ },
+ {
+ text: "Du lernst sowohl Keras als auch Transformer kennen.",
+ explain: "Korrekt, aber wir suchen nach etwas anderem :)",
+ correct: true
+ },
+ {
+ text: "Du kannst ganz einfach Metriken für den Datensatz berechnen.",
+ explain: "Keras hilft uns beim Training und der Auswertung des Modells, nicht bei der Berechnung von datensatzbezogenen Metriken."
+ }
+ ]}
+/>
+
+### 6. Wie kannst du deine eigene benutzerdefinierte Metrik definieren?
+
+tf.keras.metrics.Metric erstellen.",
+ explain: "Großartig!",
+ correct: true
+ },
+ {
+ text: "Durch Verwendung der funktionalen Keras-API.",
+ explain: "Versuch es noch einmal!"
+ },
+ {
+ text: "Durch die Verwendung einer Callable mit der Signatur metric_fn(y_true, y_pred).",
+ explain: "Korrekt!",
+ correct: true
+ },
+ {
+ text: "Indem du es googelst.",
+ explain: "Das ist nicht die Antwort, nach der wir suchen, aber es sollte dir helfen, sie zu finden.",
+ correct: true
+ }
+ ]}
+/>
+
+{/if}
diff --git a/chapters/de/chapter4/1.mdx b/chapters/de/chapter4/1.mdx
index d0130e5f3..3fbb8da19 100644
--- a/chapters/de/chapter4/1.mdx
+++ b/chapters/de/chapter4/1.mdx
@@ -1,18 +1,18 @@
-# Der Hugging Face Hub
-
-Der [Hugging Face Hub](https://huggingface.co/) –- unsere Hauptwebseite –- ist eine zentrale Platform, wo Nutzer*innen "state-of-the-art" Modelle und Datensätze entdecken, benutzen und dazu beitragen können. Eine große Vielfalt an Modellen steht öffentlich zur Verfügung auf der Platform – insgesamt mehr als 10000 Modelle. In diesem Kapitel fokusieren wir uns auf die Modelle und die Datensätze werden wir uns im Kapitel 5 anschauen.
-
-Die Modelle auf dem Hub sind nicht auf 🤗 Transformers bzw. NLP eingeschränkt.
-Es gibt Modelle von [Flair](https://github.com/flairNLP/flair) und [AllenNLP](https://github.com/allenai/allennlp) für NLP, [Asteroid](https://github.com/asteroid-team/asteroid) und [pyannote](https://github.com/pyannote/pyannote-audio) für Spracherkennung, und [timm](https://github.com/rwightman/pytorch-image-models) für Computer Vision, um ein paar Beispiele zu nennen.
-
-Jedes Modell wird als Git-Repository gehosted, was Versionierung und Reproduzierbarkeit ermöglicht. Durch das Teilen eines Modells wird dieses der Community zur Verfügung gestellt. Somit wird das Teilen und die Anwendung vom Modell einfacher und jede/r hat die Möglichkeit, das Modell zu Verwenden, ohne es selbst trainieren zu müssen.
-
-Dazu löst das Teilen eines Modells auf dem Hub automatisch das Deployment einer Hosted-Inferenz-API für das Modell aus. Jede/r in der Communinity kann das Modell direkt auf der Modellsseite mit benutzerdefinierten Inputs und passenden Widgets ausprobieren.
-
-Das Beste ist, dass sowohl das Teilen als auch das Nutzen von öffentlichen Modellen auf dem Hub völlig kostenlos erfolgt! [Bezahlte Pläne](https://huggingface.co/pricing) gibt es auch, falls du Modelle privat teilen möchtest.
-
-Das folgende Video zeigt, wie man auf dem Hub navigieren kann.
-
-
-
-Ein huggingface.co Account ist für den folgenden Teil erforderlich, da wir Repositories auf dem Hugging Face Hub erstellen und verwalten werden: [Account erstellen](https://huggingface.co/join)
+# Der Hugging Face Hub
+
+Der [Hugging Face Hub](https://huggingface.co/) –- unsere Hauptwebseite –- ist eine zentrale Platform, wo Nutzer*innen "state-of-the-art" Modelle und Datensätze entdecken, benutzen und dazu beitragen können. Eine große Vielfalt an Modellen steht öffentlich zur Verfügung auf der Platform – insgesamt mehr als 10000 Modelle. In diesem Kapitel fokusieren wir uns auf die Modelle und die Datensätze werden wir uns im Kapitel 5 anschauen.
+
+Die Modelle auf dem Hub sind nicht auf 🤗 Transformers bzw. NLP eingeschränkt.
+Es gibt Modelle von [Flair](https://github.com/flairNLP/flair) und [AllenNLP](https://github.com/allenai/allennlp) für NLP, [Asteroid](https://github.com/asteroid-team/asteroid) und [pyannote](https://github.com/pyannote/pyannote-audio) für Spracherkennung, und [timm](https://github.com/rwightman/pytorch-image-models) für Computer Vision, um ein paar Beispiele zu nennen.
+
+Jedes Modell wird als Git-Repository gehosted, was Versionierung und Reproduzierbarkeit ermöglicht. Durch das Teilen eines Modells wird dieses der Community zur Verfügung gestellt. Somit wird das Teilen und die Anwendung vom Modell einfacher und jede/r hat die Möglichkeit, das Modell zu Verwenden, ohne es selbst trainieren zu müssen.
+
+Dazu löst das Teilen eines Modells auf dem Hub automatisch das Deployment einer Hosted-Inferenz-API für das Modell aus. Jede/r in der Communinity kann das Modell direkt auf der Modellsseite mit benutzerdefinierten Inputs und passenden Widgets ausprobieren.
+
+Das Beste ist, dass sowohl das Teilen als auch das Nutzen von öffentlichen Modellen auf dem Hub völlig kostenlos erfolgt! [Bezahlte Pläne](https://huggingface.co/pricing) gibt es auch, falls du Modelle privat teilen möchtest.
+
+Das folgende Video zeigt, wie man auf dem Hub navigieren kann.
+
+
+
+Ein huggingface.co Account ist für den folgenden Teil erforderlich, da wir Repositories auf dem Hugging Face Hub erstellen und verwalten werden: [Account erstellen](https://huggingface.co/join)
diff --git a/chapters/de/chapter4/2.mdx b/chapters/de/chapter4/2.mdx
index 082e5040c..bc1a9a049 100644
--- a/chapters/de/chapter4/2.mdx
+++ b/chapters/de/chapter4/2.mdx
@@ -1,95 +1,95 @@
-
-
-# Verwendung vortrainierter Modelle
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-
-Der Model Hub erleichtert das Auswählen des passenden Modells, sodass es von downstream Libraries mit wenigen Codezeilen benutzt werden kann. Lass uns anschauen, wie genau man solche Modelle verwendet und wie man der Communinity zurück beitragen kann.
-
-Nehmen wir an, wir suchen nach einem französichbasierten Modell, das die "mask filling" Aufgabe kann.
-
-
-

-
-
-Wir wählen den `camembert-base` Checkpoint aus, um es zu auszuprobieren. Das Kennzeichen `camembert-base` ist alles, was wir brauchen, um loszulegen! Wie in früheren Kapiteln gezeigt wurde, können wir das Modell mit der `pipeline()` Funktion instanziieren:
-
-```py
-from transformers import pipeline
-
-camembert_fill_mask = pipeline("fill-mask", model="camembert-base")
-results = camembert_fill_mask("Le camembert est :)")
-```
-
-```python out
-[
- {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'},
- {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'},
- {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'},
- {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'},
- {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'}
-]
-```
-So einfach kann man mit einer Pipeline ein Modell laden. Dabei muss man nur darauf achten, den passenden Checkpoint für die gewünschte Aufgabe zu selektieren. Zum Beispiel: Wir laden hier den `camembert-base` Checkpoint in die `fill-mask` Pipeline, was schon korrekt ist. Aber würden wir diesen Checkpoint in die `text-classification` Pipeline laden, wären die Ergebnisse völlig sinnlos, weil der "head" von `camembert-base` für diese Aufgabe einfach nicht passt! Wir empfehlen, den "Task Selector" auf der Hugging Face Hub Seite zu benutzen, um die richtigen Checkpoints auszuwählen:
-
-
-

-
-
-Du kannst auch den Checkpoint mit der Modell-Architektur direkt instanziieren:
-
-{#if fw === 'pt'}
-```py
-from transformers import CamembertTokenizer, CamembertForMaskedLM
-
-tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
-model = CamembertForMaskedLM.from_pretrained("camembert-base")
-```
-
-Dennoch empfehlen wir, dass man die [`Auto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes) stattdessen benutzt, da diese architekturunabhängig sind. Das vorherige Code-Beispiel gilt nur für Checkpoints, die in die CamemBERT Architektur zu laden sind, aber mit den `Auto*` Klassen kann man Checkpoints ziemlich einfach tauschen:
-
-```py
-from transformers import AutoTokenizer, AutoModelForMaskedLM
-
-tokenizer = AutoTokenizer.from_pretrained("camembert-base")
-model = AutoModelForMaskedLM.from_pretrained("camembert-base")
-```
-{:else}
-```py
-from transformers import CamembertTokenizer, TFCamembertForMaskedLM
-
-tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
-model = TFCamembertForMaskedLM.from_pretrained("camembert-base")
-```
-
-Hier empfehlen wir auch, dass man stattdessen die [`TFAuto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes) benutzt, da diese architekturunabhängig sind. Das vorherige Code-Beispiel gilt nur für Checkpoints, die in die CamemBERT Architektur zu laden sind, aber mit den `TFAuto*` Klassen kann man Checkpoints einfach tauschen:
-
-```py
-from transformers import AutoTokenizer, TFAutoModelForMaskedLM
-
-tokenizer = AutoTokenizer.from_pretrained("camembert-base")
-model = TFAutoModelForMaskedLM.from_pretrained("camembert-base")
-```
-{/if}
-
-> [!TIP]
-> Wenn du ein vortrainiertes Modell verwendest, prüf erstmal, wie genau das traininert wurde, mit welchen Datensätzen, sowie seine Einschränkungen und Biases. All diese Informationen sollten auf der Modellbeschreibungskarte stehen.
+
+
+# Verwendung vortrainierter Modelle
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+
+Der Model Hub erleichtert das Auswählen des passenden Modells, sodass es von downstream Libraries mit wenigen Codezeilen benutzt werden kann. Lass uns anschauen, wie genau man solche Modelle verwendet und wie man der Communinity zurück beitragen kann.
+
+Nehmen wir an, wir suchen nach einem französichbasierten Modell, das die "mask filling" Aufgabe kann.
+
+
+

+
+
+Wir wählen den `camembert-base` Checkpoint aus, um es zu auszuprobieren. Das Kennzeichen `camembert-base` ist alles, was wir brauchen, um loszulegen! Wie in früheren Kapiteln gezeigt wurde, können wir das Modell mit der `pipeline()` Funktion instanziieren:
+
+```py
+from transformers import pipeline
+
+camembert_fill_mask = pipeline("fill-mask", model="camembert-base")
+results = camembert_fill_mask("Le camembert est :)")
+```
+
+```python out
+[
+ {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'},
+ {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'},
+ {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'},
+ {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'},
+ {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'}
+]
+```
+So einfach kann man mit einer Pipeline ein Modell laden. Dabei muss man nur darauf achten, den passenden Checkpoint für die gewünschte Aufgabe zu selektieren. Zum Beispiel: Wir laden hier den `camembert-base` Checkpoint in die `fill-mask` Pipeline, was schon korrekt ist. Aber würden wir diesen Checkpoint in die `text-classification` Pipeline laden, wären die Ergebnisse völlig sinnlos, weil der "head" von `camembert-base` für diese Aufgabe einfach nicht passt! Wir empfehlen, den "Task Selector" auf der Hugging Face Hub Seite zu benutzen, um die richtigen Checkpoints auszuwählen:
+
+
+

+
+
+Du kannst auch den Checkpoint mit der Modell-Architektur direkt instanziieren:
+
+{#if fw === 'pt'}
+```py
+from transformers import CamembertTokenizer, CamembertForMaskedLM
+
+tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
+model = CamembertForMaskedLM.from_pretrained("camembert-base")
+```
+
+Dennoch empfehlen wir, dass man die [`Auto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes) stattdessen benutzt, da diese architekturunabhängig sind. Das vorherige Code-Beispiel gilt nur für Checkpoints, die in die CamemBERT Architektur zu laden sind, aber mit den `Auto*` Klassen kann man Checkpoints ziemlich einfach tauschen:
+
+```py
+from transformers import AutoTokenizer, AutoModelForMaskedLM
+
+tokenizer = AutoTokenizer.from_pretrained("camembert-base")
+model = AutoModelForMaskedLM.from_pretrained("camembert-base")
+```
+{:else}
+```py
+from transformers import CamembertTokenizer, TFCamembertForMaskedLM
+
+tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
+model = TFCamembertForMaskedLM.from_pretrained("camembert-base")
+```
+
+Hier empfehlen wir auch, dass man stattdessen die [`TFAuto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes) benutzt, da diese architekturunabhängig sind. Das vorherige Code-Beispiel gilt nur für Checkpoints, die in die CamemBERT Architektur zu laden sind, aber mit den `TFAuto*` Klassen kann man Checkpoints einfach tauschen:
+
+```py
+from transformers import AutoTokenizer, TFAutoModelForMaskedLM
+
+tokenizer = AutoTokenizer.from_pretrained("camembert-base")
+model = TFAutoModelForMaskedLM.from_pretrained("camembert-base")
+```
+{/if}
+
+> [!TIP]
+> Wenn du ein vortrainiertes Modell verwendest, prüf erstmal, wie genau das traininert wurde, mit welchen Datensätzen, sowie seine Einschränkungen und Biases. All diese Informationen sollten auf der Modellbeschreibungskarte stehen.
diff --git a/chapters/de/chapter4/3.mdx b/chapters/de/chapter4/3.mdx
index 6a9b99441..99eee5a85 100644
--- a/chapters/de/chapter4/3.mdx
+++ b/chapters/de/chapter4/3.mdx
@@ -1,632 +1,632 @@
-
-
-# Vortrainierte Modelle teilen
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Demnächst schauen wir uns an, wie man am einfachsten vortrainierte Modelle auf dem 🤗 Hub teilen kann.
-Es gibt schon Tools und Hilfsmittel, die das Teilen und Updaten von Modellen auf dem Hub vereinfachen. Die werden wir gleich unten explorieren.
-
-
-
-Wir empfehlen allen Nutzer:innen, die Modelle trainieren, dass sie der Communinity beitragen, indem sie Modelle teilen. Selbst die Modelle, die auf sehr spezifische Datensätze trainiert wurden, werden anderen Nutzer:innen helfen, weil man Zeit und Rechenressourcen spart und Zugang zu nützlichen Trainingsartifakten bekommt. Also eventuell kannst du auch von der Arbeit anderer Nutzer:innen auch profitieren!
-
-Es gibt drei Wege, um Repositories zu neuen Modellen zu kreieren:
-
-- Mittels der `push_to_hub` API
-- Mittels der `huggingface_hub` Python Bibliothek
-- Mittels der Web-Oberfläche
-
-Nachdem du einen Repository erstellst hast, kannst du die Dateien über git und git-lfs hochladen. Demnächst zeigen wir dir die genauen Schritte, um Modell-Repositories zu erstellenund Dateien hochzuladen.
-
-
-## Hochladen mit der `push_to_hub` API
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-Die einfachste Variante, um Dateien auf den Hub hochzuladen, ist mittels der `push_to_hub` API. Bevor du weitermachst, must du einen Autentifizierungstoken generieren, damit die `huggingface_hub` API weißt, wer du bist und auf welche Namespaces du zugreifen darfst. Stell sicher, dass du in einer Umgebung mit `transformers` installiert bist (siehe [Setup](/course/chapter0)). Wenn du auf einem Notebook bist, kannst du diese Funktion benutzen, um dich einzuloggen:
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-Im Terminal kannst folgendes ausführen:
-
-```bash
-huggingface-cli login
-```
-
-In beiden Fällen solltest du nach deinem Username und Passwort gefragt werden. Das sind die selben, mit denen du dich auf dem Hub einloggst. Solltest du noch kein Hub-Profil haben, musst du erstmal eins [hier](https://huggingface.co/join) erstellen.
-
-Großartig! Nun hast du deinen Autentifizierungstoken in deinem Cache-Ordner gespeichert. Lass uns ein paar Repositories erstellen!
-
-{#if fw === 'pt'}
-
-Wenn du schon Modelle mit der `Trainer` API trainiert hast, dann ist der einfachste Weg, um Modelle hochzuladen, das Argument `push_to_hub=True` in `TrainingArguments` einzustellen.
-
-```py
-from transformers import TrainingArguments
-
-training_args = TrainingArguments(
- "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True
-)
-```
-
-Wenn du `trainer.train()` aufrufst, lädt der `Trainer` das Modell auf den Hub zu dem Repository in deinem Namespace hoch. Das passiert jedes Mal, wenn das Modell gespeichert wird (in diesem Beispiel jede Epoche). Der Repository wird so benannt werden, wie der Output-Ordner, den du gewählt hast (hier `bert-finetuned-mrpc`). Natürlich kannst du dir aber einen anderen Namen ausdenken und mit `hub_model_id = "a_different_name"` setzen.
-
-Um dein Modell zu einer Organisation, wovon du Mitglied bist, hochzuladen, kannst du einfach `hub_model_id = "my_organization/my_repo_name"` mit eingeben.
-
-Wenn das Training durch ist, must du noch einmal `trainer.push_to_hub()` ausführen, um die letzte Version deines Modells hochzuladen. Das wird auch eine Modell-Karte generieren, auf der die relevanten Metadaten mit den benutzten Hyperparametern und Evaluierungsergebnissen! Hier ist ein Beispiel von dem Inhalt, den du auf so einer Modell-Karte finden kannst:
-
-
-

-
-
-{:else}
-
-Wenn du für das Modell-Training Keras benutzt, ist der einfachste Weg, um das Modell aud den Hub hochzuladen, den `PushToHubCallback` zu setzen, wenn du `model.fit()` aufrufst.
-
-```py
-from transformers import PushToHubCallback
-
-callback = PushToHubCallback(
- "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer
-)
-```
-
-Danach must du noch `callbacks=[callback]` beim `model.fit()` Aufruf setzen.
-Der Callback wird das Modell auf den Hub hochladen und zwar zu einem Repository in deinem Namespace. Das passiert jedes Mal, wenn das Modell gespeichert wird (in diesem Beispiel jede Epoche). Der Repository wird so benannt werden, wie der Output-Ordner, den du gewählt hast (hier `bert-finetuned-mrpc`). Natürlich kannst du dir aber einen anderen Namen ausdenken und mit `hub_model_id = "a_different_name"` setzen.
-
-Um dein Modell zu einer Organisation, wovon du Mitglied bist, hochzuladen, kannst du einfach `hub_model_id = "my_organization/my_repo_name"` mit eingeben.
-
-
-{/if}
-
-Auf einer tieferen Ebene kann man auf Modelle, Tokenizers und Konfigurationen auf dem Model-Hub direkt zugreifen, indem man die Methode `push_to_hub()` benutzt.
-Diese Methode kümmert sich sowohl um das Erstellen vom Repository als auch das Pushen (Hochladen) von Modell- und Tokenizer-Dateien auf den Repository. Also da ist kein manueller Schritt notwendig (im Gegensatz zu den APIs, die wir demnächst sehen werden).
-
-Um uns eine Vorstellung zu schaffen, wie es funktioniert, lass uns zuerst ein Modell und einen Tokenizer initialisieren:
-
-{#if fw === 'pt'}
-```py
-from transformers import AutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = AutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-```
-{:else}
-```py
-from transformers import TFAutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-```
-{/if}
-
-Dir steht frei, was du mit diesen machst, z.B. Tokens zum Tokenizer hinzuzufügen, das Modell zu trainineren oder zu finetunen. Wenn du mit dem Modell, Gewichten und Tokenizer zufrieden bist, kannst du die Methode `push_to_hub()` vom `model` Objekt benutzten:
-
-```py
-model.push_to_hub("dummy-model")
-```
-Das wird den neuen Repository `dummy-model` in deinem Profil erstellen und den mit deinen Model-Dateien befüllen. Mach das gliche mit dem Tokenizer, sodass jetzt alle Dateien in diesem Repository verfügbar sind.
-
-```py
-tokenizer.push_to_hub("dummy-model")
-```
-Wenn du Teil einer Organisation bist, kannst du einfach das Argument `organization` mit eingeben, um die Artifakte auf den Namespace dieser Organisation hochzuladen.
-
-```py
-tokenizer.push_to_hub("dummy-model", organization="huggingface")
-```
-
-Wenn du einen bestimmten Hugging Face Token benutzten möchtest, kannst du ihn auch in der Methode `push_to_hub()` spezifizieren:
-
-```py
-tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="")
-```
-
-Nun geh auf den Model Hub, um dein hochgeladenes Modell zu finden: *https://huggingface.co/user-or-organization/dummy-model*.
-
-Click auf den Tab "Files and versions" und da solltest du die Dateien finden, die auf diesem Screenshot zu sehen sind:
-
-{#if fw === 'pt'}
-
-

-
-{:else}
-
-

-
-{/if}
-
-> [!TIP]
-> ✏️ **Probier das selber aus!** Lade das Modell und den Tokenizer vom Checkpoint `bert-base-cased` mit der Methode `push_to_hub()` hoch. Überprüfe, dass der Repository auf deiner Seite richtig erscheint, bevor du den löschst.
-
-Wie du schon gesehen hast, akzeptiert die Methode `push_to_hub()` mehrere Argumente. Dies erlaub das Hochladen auf den Namespace eines spezifischen Repositorys oder einer Organisation, sowie die Möglichkeit, einen anderen API Token zu benutzten. Wir empfehlen dir, die Dokumentation der Methode direkt auf [🤗 Transformers documentation](https://huggingface.co/transformers/model_sharing.html) zu lesen, um dir eine Vorstellung zu schaffen, was alles damit möglich ist.
-
-Die `push_to_hub()` Methode funktioniert im Hintergrund mit der Python Bibliothek [`huggingface_hub`](https://github.com/huggingface/huggingface_hub), die eine direkte API zum Hugging Face Hub anbietet. Sie ist auch drin in der 🤗 Transformers Bibliothek und mehreren anderen Machine Learning Bibliotheken, z.B. [`allenlp`](https://github.com/allenai/allennlp). Obwohl wir in diesem Kapitel den Fokus auf die Integration mit 🤗 Transformers legen, kannst du es in deinen eigenen Code bzw. eigene Bibliothek relativ einfach integrieren. Spring auf den letzten Part, um zu erfahren, wie man Dateien auf einen frisch erstellten Repository hochladen kann!
-
-## Verwendung der `huggingface_hub` Python Bibliothek
-Die `huggingface_hub` Python Bibliothek ist ein Python Packet, das einige Werkzeuge für das Nutzen von Modell- und Datasethub anbietet. Es bietet simple Methoden und Klassen für gängige Aufgaben, z.B. um Information zu Repositories auf dem Hub zu bekommen oder um sie zu Verwalten. Es bietet auch simple auf git basierende APIs, um die Inhalte von solchen Repositories zu verwalten sowie um den Hub in deine Projekte und Bibliotheken zu integrieren.
-
-Ähnlich wie bei der Verwendung der`push_to_hub` API ist es bei diesen Aktionen erforderlich, dass dein API Token schon in deinem Cache gespeichert ist. Dafür musst du den `login` Befehl aus der CLI ausführen so wie in dem vorherigen Teil erklärt wurde (nochmal: Vergiss nicht, das `!` Zeichen vor die Befehle zu setzen, wenn du im Google Colab arbeitest).
-
-```bash
-huggingface-cli login
-```
-
-Die `huggingface_hub` Bibliothek bietet mehrere nützliche Methoden und Klassen an. Erstens gibt es einige Methoden, um das Erstellen, Löschen, usw. von Repositories durchzuführen:
-
-
-```python no-format
-from huggingface_hub import (
- # User-Management
- login,
- logout,
- whoami,
-
- # Repository erstellen und managen
- create_repo,
- delete_repo,
- update_repo_visibility,
-
- # Methoden, um inhaltliche Information abzufragen/abzuändern
- list_models,
- list_datasets,
- list_metrics,
- list_repo_files,
- upload_file,
- delete_file,
-)
-```
-
-Außerdem gibt es die sehr mächtige `Repository` Klasse, um einen lokalen Repository zu managen. Demnächst werden wir uns mit diesen Methoden und dieser Klasse beschäftigen, um zu verstehen, wie man die am besten nutzt.
-
-Mit der `create_repo` Methode kann ein neuer Repository auf dem Hub erstellt werden:
-
-```py
-from huggingface_hub import create_repo
-
-create_repo("dummy-model")
-```
-
-Das erstellt den Repository `dummy-model` unter deinem Namespace. Wenn du möchtest, kannst du auch die Organisation spezifizieren, zu der der Repository gehören sollte, indem du das `organization` Argument setzt:
-
-```py
-from huggingface_hub import create_repo
-
-create_repo("dummy-model", organization="huggingface")
-```
-
-Das erstellt den Repository `dummy-model` unter dem `huggingface` Namespace – angenommen du gehörst zu dieser Organisation.
-Andere eventuell nützliche Argumente sind:
-
-- `private`: um zu spezifizieren, ob der Repository für andere sichtbar sein sollte oder nicht.
-- `token`: um den Token, der im Zwischenspeicher (Cache) liegt, mit einem neuen Token zu überscheiben.
-- `repo_type`: zum Auswählen, ob du einen `dataset` oder einen `space` anstatt von einem Modell kreieren möchtest. Erlaubte Werte sind `"dataset"` und `"space"`.
-
-Nachdem der Repository erstellt wurde, können wir Dateien hinzufügen! Spring zum nächsten Abschnitt, um drei Varianten dazu zu lernen, wie man das machen kann.
-
-## Mit der Webinterface
-
-Die Webinterface bietet Tools an, um Repositories direkt auf dem Hub zu managen. Damit kannst du ganz einfach Repositories erstellen, Dateien hinzufügen (sogar große Dateien), Modelle explorieren, Unterschiede ("diffs") visualisieren und viel mehr.
-
-Um einen Repository zu erstellen, geh auf [huggingface.co/new](https://huggingface.co/new):
-
-
-

-
-
-Erstens muss man den Besitzer vom Repository eingeben: Das kannst entweder du selbst oder jede andere Person von der Organisation sein, zu der du gehörst. Wenn du eine Organisation auswählst, wird das Modell auf der Seite der Organisation präsentiert und jedes Mitglied der Organisation wird zu diesem Repository beitragen können.
-
-Als nächstes gib den Namen deines Modells ein. So wird der Repository auch heißen. Zuletzt kannst du spezifizieren, ob das Modell öffentlich oder privat sein soll. Private Modelle sind von der Öffentlichkeit unsichtbar.
-
-Nach der Erstellung des Repositorys solltest du so eine Seite sehen können:
-
-
-

-
-
-Hier wird dein Modell gehostet. Um mit dem Auffüllen zu beginnen, kannst du direkt über die Weboberfläche eine README-Datei hinzufügen.
-
-
-

-
-
-Die README-Datei ist im Markdown Format — du kannst dich damit gerne austoben!
-Der dritte Teil dieses Kapitels zielt darauf hin, eine "model card" (Steckbrief) zu bauen. Steckbriefe haben eine entscheidende Relevanz, um dein Modell wertvoll zu machen, denn du kannst dort anderen erzählen, was das Modell kann.
-
-Wenn du dir den "Files and versions" Tab anschaust, wirst du sehen, dass noch nicht viele Dateien darauf sind – nämlich nur die von dir eben kreierte *README.md* und die *.gitattributes* (wo große Dateien geloggt werden).
-
-
-
-

-
-
-Gleich werden wir sehen, wie wir neue Dateien hinzufügen können.
-
-## Hochladen von Modell-Dateien
-
-Das System zum Managen der Dateien auf Hugging Face Hub basiert auf git für normale Dateien und auf git-lfs ([Git Large File Storage](https://git-lfs.github.com/)) für größere Dateien.
-
-Im nächsten Teil schauen wir uns drei Möglichkeitein an, um Dateien mittels `huggingface_hub` und git-Befehle auf den Hub hochzuladen.
-
-### Die `upload_file` Variante
-
-Um `upload_file` zu verwenden, muss man nicht unbedingt git und git-lfs installiert haben. Die Funktion lädt Dateien auf den 🤗 Hub mittels HTTP POST Anfragen. Eine Einschränkunf dieser Variante ist, dass man nur mit Dateien unter 5GB groß arbeiten kann.
-Wenn deine Dateien größer als 5GB sind, nutz eine von den folgenden zwei Methoden.
-
-Die API kann folgendermaßen benutzt werden:
-
-```py
-from huggingface_hub import upload_file
-
-upload_file(
- "/config.json",
- path_in_repo="config.json",
- repo_id="/dummy-model",
-)
-```
-Das wird die `config.json` Datei in `` auf das Root-Verzeichnis vom Repository als `config.json` vom `dummy-model` Repository.
-Andere nützliche Argumente :
-
-- `token`, um den Token zu überscheiben, der in deinem Cache gespeichert ist
-- `repo_type`, wenn du anstatt von einem Modell Dateien auf einen `dataset` oder `space` hochladen möchtest. Valide Werte sind `"dataset"` und `"space"`.
-
-
-### Die `Repository` Klasse
-
-Die `Repository` Klasse verwaltet einen lokalen Repository so wie git. Sie abstrahiert aber die meisten schwierigen Punkte, auf die man stoßen würde, wenn man eine ähnliche Funktionalität mit git erreichen möchte.
-
-Diese Klasse braucht git und git-lfs im System schon installiert. Also stell sicher, dass du git-lfs installiert hast (siehe [hier](https://git-lfs.github.com/) für Installationsanweisungen) und richte alles ein, bevor du loslegst.
-
-Um mit dem Repository rumspielen zu starten, können wir den in einem lokalen Ordner initialisieren, in dem wir den Remote-Repository klonen:
-
-```py
-from huggingface_hub import Repository
-
-repo = Repository("", clone_from="/dummy-model")
-```
-
-Das hat den Ordner `` in unserem Arbeitsverzeichnis erstellt. Dieser Ordner enthält bisher nur die `.gitattributes` Datel, da diese die einzige Datei ist, die wir mit `create_repo` kreiert haben.
-
-Ab jetzt können mehrere gängige Methoden benutzten:
-
-```py
-repo.git_pull()
-repo.git_add()
-repo.git_commit()
-repo.git_push()
-repo.git_tag()
-```
-
-Und andere Optionen auch! Wir empfehlen, dass du dir die Dokumentation zu `Repository`, die dir [hier](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management) zur Verfügung steht, anschaust, um dir eine Übersicht aller verfügbaren Methoden zu verschaffen.
-
-Bisher haben wir ein Modell und einen Tokenizer, die wir gerne auf den Hub pushen würden. Wir haben auch den Repository geklont, sodass wir die Dateien in dem Repository speichern können.
-
-Zuerst stellen wir sicher, dass unser lokaler Repository einen aktuellen Stand hat, in dem wir die letzten Änderungen pullen:
-
-```py
-repo.git_pull()
-```
-
-Wenn das durch ist, speichern wir die Dateien vom Modell und Tokenizer:
-
-```py
-model.save_pretrained("")
-tokenizer.save_pretrained("")
-```
-
-Der Pfad `` beinhaltet jetzt alle Modell- und Tokenizerdateien. Wir folgen dem gängigen Git-Workflow, indem wir die Dateien in die "staging area" bringen, wir committen und pushen sie auf den hub:
-
-```py
-repo.git_add()
-repo.git_commit("Add model and tokenizer files")
-repo.git_push()
-```
-
-Glückwunsch! Du hast gerade deine ersten Dateien auf den Hub hochgeladen.
-
-### Die git-basierte Variante
-
-Das ist der einfachste Weg zum Hochladen von Dateien: Wir werden es direkt mit git und git-lfs tun. Der Größtenteil der Schwierigkeit wird durch die früheren Ansätze abstrahiert, aber es gibt ein paar Vorbehalte bei der folgenden Methode, deswegen werden wir einem komplexeren Anwendungsfall folgen.
-
-Um diese Klasse zu benutzten, mussen wir git und git-lfs installiert haben. Also stell sicher, dass du [git-lfs](https://git-lfs.github.com/) installiert und aufgesetzt hast, bevor du beginst.
-
-Zuerst initialisiere git-lfs:
-
-```bash
-git lfs install
-```
-
-```bash
-Updated git hooks.
-Git LFS initialized.
-```
-
-Danach musst du den Modell-Repository klonen:
-
-```bash
-git clone https://huggingface.co//
-```
-
-Mein Username ist `lysandre` und ich habe den Modellnamen `dummy` benutzt. Also bei bei sieht der Befehl so aus:
-
-```
-git clone https://huggingface.co/lysandre/dummy
-```
-
-Ich habe jetzt einen Ordner namens *dummy* in meinem Arbeitsverzeichnis. Ich kann jetzt `cd` in den Ordner und mir den Inhalt anschauen:
-
-```bash
-cd dummy && ls
-```
-
-```bash
-README.md
-```
-
-Wenn du eben einen Repository mit der Hugging Face Hubs Methode `create_repo` erstellt hast, dann sollte dieser Ordner nur eine versteckte `.gitattributes` Datei enthalten. Wenn du es nach den Anweisungen in dem vorherigen Abschnitt mittels der Webinterface gemacht hast, dann sollte der Ordner eine einzige README.md Datei neben der `.gitattributes` enthalten – so wie hier angezeigt wird.
-
-Das Hinzufügen einer Datei mit normaler Größe, z.B. Konfiguration- oder Vokabulardatei, wird so gemach wie in einem git-basierten System. Aber größere Dateien müssen mit git-lfs registriert werden, um sie zu *huggingface.co* zu pushen.
-
-Lass uns kurz zurück zu Python, um ein Modell und einen Tokenizer zu generieren, die wir zu unserem dummy repository committen möchten:
-
-{#if fw === 'pt'}
-```py
-from transformers import AutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = AutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-# Mach was du möchtest mit dem Modell, z.B. trainieren, fine-tunen.
-
-model.save_pretrained("")
-tokenizer.save_pretrained("")
-```
-{:else}
-```py
-from transformers import TFAutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-# Mach was du möchtest mit dem Modell, z.B. trainieren, fine-tunen.
-
-model.save_pretrained("")
-tokenizer.save_pretrained("")
-```
-{/if}
-
-Jetzt haben wir die Modell- und Tokenizer-Artifakte gespeichert und können wir uns nochmal den *dummy* Ordner anschauen:
-
-```bash
-ls
-```
-
-{#if fw === 'pt'}
-```bash
-config.json pytorch_model.bin README.md sentencepiece.bpe.model special_tokens_map.json tokenizer_config.json tokenizer.json
-```
-
-Wenn du dir die Dateigrößen anschaust (z.B. mit `ls -lh`), solltest du sehen, dass die Modell-Statedict Datei (*pytorch_model.bin*) der einzige Ausreißer ist mit über 400 MB.
-
-{:else}
-```bash
-config.json README.md sentencepiece.bpe.model special_tokens_map.json tf_model.h5 tokenizer_config.json tokenizer.json
-```
-
-Wenn du dir die Dateigrößen anschaust (z.B. mit `ls -lh`), solltest du sehen, dass die Modell-Statedict Datei (*t5_model.h5*) der einzige Ausreißer ist mit über 400 MB.
-
-{/if}
-
-> [!TIP]
-> ✏️ Wenn ein Repository mittels der Webinterface kreiert wird, wird die *.gitattributes* Datei automatisch gesetzt, um bestimmte Dateiendungen wie *.bin* und *.h5* als große Dateien zu betrachten, sodass git-lfs sie tracken kann, ohne dass du weiteres konfigurieren musst.
-
-Nun können wir weitermachen und so arbeiten wie wir es mit normalen Git Repositories machen. Wir können die Dateien stagen mit dem Git-Befehl `git add`:
-
-```bash
-git add .
-```
-
-Jetzt schauen wir, welche Dateien gestaged wurden:
-
-```bash
-git status
-```
-
-{#if fw === 'pt'}
-```bash
-On branch main
-Your branch is up to date with 'origin/main'.
-
-Changes to be committed:
- (use "git restore --staged ..." to unstage)
- modified: .gitattributes
- new file: config.json
- new file: pytorch_model.bin
- new file: sentencepiece.bpe.model
- new file: special_tokens_map.json
- new file: tokenizer.json
- new file: tokenizer_config.json
-```
-{:else}
-```bash
-On branch main
-Your branch is up to date with 'origin/main'.
-
-Changes to be committed:
- (use "git restore --staged ..." to unstage)
- modified: .gitattributes
- new file: config.json
- new file: sentencepiece.bpe.model
- new file: special_tokens_map.json
- new file: tf_model.h5
- new file: tokenizer.json
- new file: tokenizer_config.json
-```
-{/if}
-
-Ähnlicherweise können wir sicherstellen, dass git-lfs die richtigen Dateien trackt mit dem `status` Befehl:
-
-```bash
-git lfs status
-```
-
-{#if fw === 'pt'}
-```bash
-On branch main
-Objects to be pushed to origin/main:
-
-
-Objects to be committed:
-
- config.json (Git: bc20ff2)
- pytorch_model.bin (LFS: 35686c2)
- sentencepiece.bpe.model (LFS: 988bc5a)
- special_tokens_map.json (Git: cb23931)
- tokenizer.json (Git: 851ff3e)
- tokenizer_config.json (Git: f0f7783)
-
-Objects not staged for commit:
-
-
-```
-
-Da sehen wir, dass alle Dateien `Git` als Handler haben. Nur die *pytorch_model.bin* und *sentencepiece.bpe.model* Dateien haben `LFS`. Toll!
-
-{:else}
-```bash
-On branch main
-Objects to be pushed to origin/main:
-
-
-Objects to be committed:
-
- config.json (Git: bc20ff2)
- sentencepiece.bpe.model (LFS: 988bc5a)
- special_tokens_map.json (Git: cb23931)
- tf_model.h5 (LFS: 86fce29)
- tokenizer.json (Git: 851ff3e)
- tokenizer_config.json (Git: f0f7783)
-
-Objects not staged for commit:
-
-
-```
-
-Da sehen wir, dass alle Dateien `Git` als Handler haben. Nur die *t5_model.h5* hat `LFS`. Sehr gut!
-
-{/if}
-
-Lass uns mit den letzten Schritten weitermachen, indem wir die Änderungen commiten und zum *huggingface.co* Remote-Repository pushen:
-
-```bash
-git commit -m "First model version"
-```
-
-{#if fw === 'pt'}
-```bash
-[main b08aab1] First model version
- 7 files changed, 29027 insertions(+)
- 6 files changed, 36 insertions(+)
- create mode 100644 config.json
- create mode 100644 pytorch_model.bin
- create mode 100644 sentencepiece.bpe.model
- create mode 100644 special_tokens_map.json
- create mode 100644 tokenizer.json
- create mode 100644 tokenizer_config.json
-```
-{:else}
-```bash
-[main b08aab1] First model version
- 6 files changed, 36 insertions(+)
- create mode 100644 config.json
- create mode 100644 sentencepiece.bpe.model
- create mode 100644 special_tokens_map.json
- create mode 100644 tf_model.h5
- create mode 100644 tokenizer.json
- create mode 100644 tokenizer_config.json
-```
-{/if}
-
-Das Pushen kann ein bisschen dauern, je nach dem wie schnell deine Internetverbindung ist und wie groß deine Dateien sind:
-
-```bash
-git push
-```
-
-```bash
-Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done.
-Enumerating objects: 11, done.
-Counting objects: 100% (11/11), done.
-Delta compression using up to 12 threads
-Compressing objects: 100% (9/9), done.
-Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done.
-Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
-To https://huggingface.co/lysandre/dummy
- 891b41d..b08aab1 main -> main
-```
-
-{#if fw === 'pt'}
-Wenn alles durch ist, können wir uns den Repository anschauen und die eben hinzugefügten Dateien finden:
-
-
-

-
-
-Mit der UI kannst du die Modell-Dateien und die Commits explorieren, um die Differenz bei jedem Commit zu sehen:
-
-
-

-
-{:else}
-
-Wenn alles durch ist, können wir uns den Repository anschauen und die eben hinzugefügten Dateien finden:
-
-
-

-
-
-Mit der UI kannst du die Modell-Dateien und die Commits explorieren, um die Differenz bei jedem Commit zu sehen:
-
-
-

-
-{/if}
+
+
+# Vortrainierte Modelle teilen
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Demnächst schauen wir uns an, wie man am einfachsten vortrainierte Modelle auf dem 🤗 Hub teilen kann.
+Es gibt schon Tools und Hilfsmittel, die das Teilen und Updaten von Modellen auf dem Hub vereinfachen. Die werden wir gleich unten explorieren.
+
+
+
+Wir empfehlen allen Nutzer:innen, die Modelle trainieren, dass sie der Communinity beitragen, indem sie Modelle teilen. Selbst die Modelle, die auf sehr spezifische Datensätze trainiert wurden, werden anderen Nutzer:innen helfen, weil man Zeit und Rechenressourcen spart und Zugang zu nützlichen Trainingsartifakten bekommt. Also eventuell kannst du auch von der Arbeit anderer Nutzer:innen auch profitieren!
+
+Es gibt drei Wege, um Repositories zu neuen Modellen zu kreieren:
+
+- Mittels der `push_to_hub` API
+- Mittels der `huggingface_hub` Python Bibliothek
+- Mittels der Web-Oberfläche
+
+Nachdem du einen Repository erstellst hast, kannst du die Dateien über git und git-lfs hochladen. Demnächst zeigen wir dir die genauen Schritte, um Modell-Repositories zu erstellenund Dateien hochzuladen.
+
+
+## Hochladen mit der `push_to_hub` API
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+Die einfachste Variante, um Dateien auf den Hub hochzuladen, ist mittels der `push_to_hub` API. Bevor du weitermachst, must du einen Autentifizierungstoken generieren, damit die `huggingface_hub` API weißt, wer du bist und auf welche Namespaces du zugreifen darfst. Stell sicher, dass du in einer Umgebung mit `transformers` installiert bist (siehe [Setup](/course/chapter0)). Wenn du auf einem Notebook bist, kannst du diese Funktion benutzen, um dich einzuloggen:
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Im Terminal kannst folgendes ausführen:
+
+```bash
+huggingface-cli login
+```
+
+In beiden Fällen solltest du nach deinem Username und Passwort gefragt werden. Das sind die selben, mit denen du dich auf dem Hub einloggst. Solltest du noch kein Hub-Profil haben, musst du erstmal eins [hier](https://huggingface.co/join) erstellen.
+
+Großartig! Nun hast du deinen Autentifizierungstoken in deinem Cache-Ordner gespeichert. Lass uns ein paar Repositories erstellen!
+
+{#if fw === 'pt'}
+
+Wenn du schon Modelle mit der `Trainer` API trainiert hast, dann ist der einfachste Weg, um Modelle hochzuladen, das Argument `push_to_hub=True` in `TrainingArguments` einzustellen.
+
+```py
+from transformers import TrainingArguments
+
+training_args = TrainingArguments(
+ "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True
+)
+```
+
+Wenn du `trainer.train()` aufrufst, lädt der `Trainer` das Modell auf den Hub zu dem Repository in deinem Namespace hoch. Das passiert jedes Mal, wenn das Modell gespeichert wird (in diesem Beispiel jede Epoche). Der Repository wird so benannt werden, wie der Output-Ordner, den du gewählt hast (hier `bert-finetuned-mrpc`). Natürlich kannst du dir aber einen anderen Namen ausdenken und mit `hub_model_id = "a_different_name"` setzen.
+
+Um dein Modell zu einer Organisation, wovon du Mitglied bist, hochzuladen, kannst du einfach `hub_model_id = "my_organization/my_repo_name"` mit eingeben.
+
+Wenn das Training durch ist, must du noch einmal `trainer.push_to_hub()` ausführen, um die letzte Version deines Modells hochzuladen. Das wird auch eine Modell-Karte generieren, auf der die relevanten Metadaten mit den benutzten Hyperparametern und Evaluierungsergebnissen! Hier ist ein Beispiel von dem Inhalt, den du auf so einer Modell-Karte finden kannst:
+
+
+

+
+
+{:else}
+
+Wenn du für das Modell-Training Keras benutzt, ist der einfachste Weg, um das Modell aud den Hub hochzuladen, den `PushToHubCallback` zu setzen, wenn du `model.fit()` aufrufst.
+
+```py
+from transformers import PushToHubCallback
+
+callback = PushToHubCallback(
+ "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer
+)
+```
+
+Danach must du noch `callbacks=[callback]` beim `model.fit()` Aufruf setzen.
+Der Callback wird das Modell auf den Hub hochladen und zwar zu einem Repository in deinem Namespace. Das passiert jedes Mal, wenn das Modell gespeichert wird (in diesem Beispiel jede Epoche). Der Repository wird so benannt werden, wie der Output-Ordner, den du gewählt hast (hier `bert-finetuned-mrpc`). Natürlich kannst du dir aber einen anderen Namen ausdenken und mit `hub_model_id = "a_different_name"` setzen.
+
+Um dein Modell zu einer Organisation, wovon du Mitglied bist, hochzuladen, kannst du einfach `hub_model_id = "my_organization/my_repo_name"` mit eingeben.
+
+
+{/if}
+
+Auf einer tieferen Ebene kann man auf Modelle, Tokenizers und Konfigurationen auf dem Model-Hub direkt zugreifen, indem man die Methode `push_to_hub()` benutzt.
+Diese Methode kümmert sich sowohl um das Erstellen vom Repository als auch das Pushen (Hochladen) von Modell- und Tokenizer-Dateien auf den Repository. Also da ist kein manueller Schritt notwendig (im Gegensatz zu den APIs, die wir demnächst sehen werden).
+
+Um uns eine Vorstellung zu schaffen, wie es funktioniert, lass uns zuerst ein Modell und einen Tokenizer initialisieren:
+
+{#if fw === 'pt'}
+```py
+from transformers import AutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = AutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+{:else}
+```py
+from transformers import TFAutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+{/if}
+
+Dir steht frei, was du mit diesen machst, z.B. Tokens zum Tokenizer hinzuzufügen, das Modell zu trainineren oder zu finetunen. Wenn du mit dem Modell, Gewichten und Tokenizer zufrieden bist, kannst du die Methode `push_to_hub()` vom `model` Objekt benutzten:
+
+```py
+model.push_to_hub("dummy-model")
+```
+Das wird den neuen Repository `dummy-model` in deinem Profil erstellen und den mit deinen Model-Dateien befüllen. Mach das gliche mit dem Tokenizer, sodass jetzt alle Dateien in diesem Repository verfügbar sind.
+
+```py
+tokenizer.push_to_hub("dummy-model")
+```
+Wenn du Teil einer Organisation bist, kannst du einfach das Argument `organization` mit eingeben, um die Artifakte auf den Namespace dieser Organisation hochzuladen.
+
+```py
+tokenizer.push_to_hub("dummy-model", organization="huggingface")
+```
+
+Wenn du einen bestimmten Hugging Face Token benutzten möchtest, kannst du ihn auch in der Methode `push_to_hub()` spezifizieren:
+
+```py
+tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="")
+```
+
+Nun geh auf den Model Hub, um dein hochgeladenes Modell zu finden: *https://huggingface.co/user-or-organization/dummy-model*.
+
+Click auf den Tab "Files and versions" und da solltest du die Dateien finden, die auf diesem Screenshot zu sehen sind:
+
+{#if fw === 'pt'}
+
+

+
+{:else}
+
+

+
+{/if}
+
+> [!TIP]
+> ✏️ **Probier das selber aus!** Lade das Modell und den Tokenizer vom Checkpoint `bert-base-cased` mit der Methode `push_to_hub()` hoch. Überprüfe, dass der Repository auf deiner Seite richtig erscheint, bevor du den löschst.
+
+Wie du schon gesehen hast, akzeptiert die Methode `push_to_hub()` mehrere Argumente. Dies erlaub das Hochladen auf den Namespace eines spezifischen Repositorys oder einer Organisation, sowie die Möglichkeit, einen anderen API Token zu benutzten. Wir empfehlen dir, die Dokumentation der Methode direkt auf [🤗 Transformers documentation](https://huggingface.co/transformers/model_sharing.html) zu lesen, um dir eine Vorstellung zu schaffen, was alles damit möglich ist.
+
+Die `push_to_hub()` Methode funktioniert im Hintergrund mit der Python Bibliothek [`huggingface_hub`](https://github.com/huggingface/huggingface_hub), die eine direkte API zum Hugging Face Hub anbietet. Sie ist auch drin in der 🤗 Transformers Bibliothek und mehreren anderen Machine Learning Bibliotheken, z.B. [`allenlp`](https://github.com/allenai/allennlp). Obwohl wir in diesem Kapitel den Fokus auf die Integration mit 🤗 Transformers legen, kannst du es in deinen eigenen Code bzw. eigene Bibliothek relativ einfach integrieren. Spring auf den letzten Part, um zu erfahren, wie man Dateien auf einen frisch erstellten Repository hochladen kann!
+
+## Verwendung der `huggingface_hub` Python Bibliothek
+Die `huggingface_hub` Python Bibliothek ist ein Python Packet, das einige Werkzeuge für das Nutzen von Modell- und Datasethub anbietet. Es bietet simple Methoden und Klassen für gängige Aufgaben, z.B. um Information zu Repositories auf dem Hub zu bekommen oder um sie zu Verwalten. Es bietet auch simple auf git basierende APIs, um die Inhalte von solchen Repositories zu verwalten sowie um den Hub in deine Projekte und Bibliotheken zu integrieren.
+
+Ähnlich wie bei der Verwendung der`push_to_hub` API ist es bei diesen Aktionen erforderlich, dass dein API Token schon in deinem Cache gespeichert ist. Dafür musst du den `login` Befehl aus der CLI ausführen so wie in dem vorherigen Teil erklärt wurde (nochmal: Vergiss nicht, das `!` Zeichen vor die Befehle zu setzen, wenn du im Google Colab arbeitest).
+
+```bash
+huggingface-cli login
+```
+
+Die `huggingface_hub` Bibliothek bietet mehrere nützliche Methoden und Klassen an. Erstens gibt es einige Methoden, um das Erstellen, Löschen, usw. von Repositories durchzuführen:
+
+
+```python no-format
+from huggingface_hub import (
+ # User-Management
+ login,
+ logout,
+ whoami,
+
+ # Repository erstellen und managen
+ create_repo,
+ delete_repo,
+ update_repo_visibility,
+
+ # Methoden, um inhaltliche Information abzufragen/abzuändern
+ list_models,
+ list_datasets,
+ list_metrics,
+ list_repo_files,
+ upload_file,
+ delete_file,
+)
+```
+
+Außerdem gibt es die sehr mächtige `Repository` Klasse, um einen lokalen Repository zu managen. Demnächst werden wir uns mit diesen Methoden und dieser Klasse beschäftigen, um zu verstehen, wie man die am besten nutzt.
+
+Mit der `create_repo` Methode kann ein neuer Repository auf dem Hub erstellt werden:
+
+```py
+from huggingface_hub import create_repo
+
+create_repo("dummy-model")
+```
+
+Das erstellt den Repository `dummy-model` unter deinem Namespace. Wenn du möchtest, kannst du auch die Organisation spezifizieren, zu der der Repository gehören sollte, indem du das `organization` Argument setzt:
+
+```py
+from huggingface_hub import create_repo
+
+create_repo("dummy-model", organization="huggingface")
+```
+
+Das erstellt den Repository `dummy-model` unter dem `huggingface` Namespace – angenommen du gehörst zu dieser Organisation.
+Andere eventuell nützliche Argumente sind:
+
+- `private`: um zu spezifizieren, ob der Repository für andere sichtbar sein sollte oder nicht.
+- `token`: um den Token, der im Zwischenspeicher (Cache) liegt, mit einem neuen Token zu überscheiben.
+- `repo_type`: zum Auswählen, ob du einen `dataset` oder einen `space` anstatt von einem Modell kreieren möchtest. Erlaubte Werte sind `"dataset"` und `"space"`.
+
+Nachdem der Repository erstellt wurde, können wir Dateien hinzufügen! Spring zum nächsten Abschnitt, um drei Varianten dazu zu lernen, wie man das machen kann.
+
+## Mit der Webinterface
+
+Die Webinterface bietet Tools an, um Repositories direkt auf dem Hub zu managen. Damit kannst du ganz einfach Repositories erstellen, Dateien hinzufügen (sogar große Dateien), Modelle explorieren, Unterschiede ("diffs") visualisieren und viel mehr.
+
+Um einen Repository zu erstellen, geh auf [huggingface.co/new](https://huggingface.co/new):
+
+
+

+
+
+Erstens muss man den Besitzer vom Repository eingeben: Das kannst entweder du selbst oder jede andere Person von der Organisation sein, zu der du gehörst. Wenn du eine Organisation auswählst, wird das Modell auf der Seite der Organisation präsentiert und jedes Mitglied der Organisation wird zu diesem Repository beitragen können.
+
+Als nächstes gib den Namen deines Modells ein. So wird der Repository auch heißen. Zuletzt kannst du spezifizieren, ob das Modell öffentlich oder privat sein soll. Private Modelle sind von der Öffentlichkeit unsichtbar.
+
+Nach der Erstellung des Repositorys solltest du so eine Seite sehen können:
+
+
+

+
+
+Hier wird dein Modell gehostet. Um mit dem Auffüllen zu beginnen, kannst du direkt über die Weboberfläche eine README-Datei hinzufügen.
+
+
+

+
+
+Die README-Datei ist im Markdown Format — du kannst dich damit gerne austoben!
+Der dritte Teil dieses Kapitels zielt darauf hin, eine "model card" (Steckbrief) zu bauen. Steckbriefe haben eine entscheidende Relevanz, um dein Modell wertvoll zu machen, denn du kannst dort anderen erzählen, was das Modell kann.
+
+Wenn du dir den "Files and versions" Tab anschaust, wirst du sehen, dass noch nicht viele Dateien darauf sind – nämlich nur die von dir eben kreierte *README.md* und die *.gitattributes* (wo große Dateien geloggt werden).
+
+
+
+

+
+
+Gleich werden wir sehen, wie wir neue Dateien hinzufügen können.
+
+## Hochladen von Modell-Dateien
+
+Das System zum Managen der Dateien auf Hugging Face Hub basiert auf git für normale Dateien und auf git-lfs ([Git Large File Storage](https://git-lfs.github.com/)) für größere Dateien.
+
+Im nächsten Teil schauen wir uns drei Möglichkeitein an, um Dateien mittels `huggingface_hub` und git-Befehle auf den Hub hochzuladen.
+
+### Die `upload_file` Variante
+
+Um `upload_file` zu verwenden, muss man nicht unbedingt git und git-lfs installiert haben. Die Funktion lädt Dateien auf den 🤗 Hub mittels HTTP POST Anfragen. Eine Einschränkunf dieser Variante ist, dass man nur mit Dateien unter 5GB groß arbeiten kann.
+Wenn deine Dateien größer als 5GB sind, nutz eine von den folgenden zwei Methoden.
+
+Die API kann folgendermaßen benutzt werden:
+
+```py
+from huggingface_hub import upload_file
+
+upload_file(
+ "/config.json",
+ path_in_repo="config.json",
+ repo_id="/dummy-model",
+)
+```
+Das wird die `config.json` Datei in `` auf das Root-Verzeichnis vom Repository als `config.json` vom `dummy-model` Repository.
+Andere nützliche Argumente :
+
+- `token`, um den Token zu überscheiben, der in deinem Cache gespeichert ist
+- `repo_type`, wenn du anstatt von einem Modell Dateien auf einen `dataset` oder `space` hochladen möchtest. Valide Werte sind `"dataset"` und `"space"`.
+
+
+### Die `Repository` Klasse
+
+Die `Repository` Klasse verwaltet einen lokalen Repository so wie git. Sie abstrahiert aber die meisten schwierigen Punkte, auf die man stoßen würde, wenn man eine ähnliche Funktionalität mit git erreichen möchte.
+
+Diese Klasse braucht git und git-lfs im System schon installiert. Also stell sicher, dass du git-lfs installiert hast (siehe [hier](https://git-lfs.github.com/) für Installationsanweisungen) und richte alles ein, bevor du loslegst.
+
+Um mit dem Repository rumspielen zu starten, können wir den in einem lokalen Ordner initialisieren, in dem wir den Remote-Repository klonen:
+
+```py
+from huggingface_hub import Repository
+
+repo = Repository("", clone_from="/dummy-model")
+```
+
+Das hat den Ordner `` in unserem Arbeitsverzeichnis erstellt. Dieser Ordner enthält bisher nur die `.gitattributes` Datel, da diese die einzige Datei ist, die wir mit `create_repo` kreiert haben.
+
+Ab jetzt können mehrere gängige Methoden benutzten:
+
+```py
+repo.git_pull()
+repo.git_add()
+repo.git_commit()
+repo.git_push()
+repo.git_tag()
+```
+
+Und andere Optionen auch! Wir empfehlen, dass du dir die Dokumentation zu `Repository`, die dir [hier](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management) zur Verfügung steht, anschaust, um dir eine Übersicht aller verfügbaren Methoden zu verschaffen.
+
+Bisher haben wir ein Modell und einen Tokenizer, die wir gerne auf den Hub pushen würden. Wir haben auch den Repository geklont, sodass wir die Dateien in dem Repository speichern können.
+
+Zuerst stellen wir sicher, dass unser lokaler Repository einen aktuellen Stand hat, in dem wir die letzten Änderungen pullen:
+
+```py
+repo.git_pull()
+```
+
+Wenn das durch ist, speichern wir die Dateien vom Modell und Tokenizer:
+
+```py
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+
+Der Pfad `` beinhaltet jetzt alle Modell- und Tokenizerdateien. Wir folgen dem gängigen Git-Workflow, indem wir die Dateien in die "staging area" bringen, wir committen und pushen sie auf den hub:
+
+```py
+repo.git_add()
+repo.git_commit("Add model and tokenizer files")
+repo.git_push()
+```
+
+Glückwunsch! Du hast gerade deine ersten Dateien auf den Hub hochgeladen.
+
+### Die git-basierte Variante
+
+Das ist der einfachste Weg zum Hochladen von Dateien: Wir werden es direkt mit git und git-lfs tun. Der Größtenteil der Schwierigkeit wird durch die früheren Ansätze abstrahiert, aber es gibt ein paar Vorbehalte bei der folgenden Methode, deswegen werden wir einem komplexeren Anwendungsfall folgen.
+
+Um diese Klasse zu benutzten, mussen wir git und git-lfs installiert haben. Also stell sicher, dass du [git-lfs](https://git-lfs.github.com/) installiert und aufgesetzt hast, bevor du beginst.
+
+Zuerst initialisiere git-lfs:
+
+```bash
+git lfs install
+```
+
+```bash
+Updated git hooks.
+Git LFS initialized.
+```
+
+Danach musst du den Modell-Repository klonen:
+
+```bash
+git clone https://huggingface.co//
+```
+
+Mein Username ist `lysandre` und ich habe den Modellnamen `dummy` benutzt. Also bei bei sieht der Befehl so aus:
+
+```
+git clone https://huggingface.co/lysandre/dummy
+```
+
+Ich habe jetzt einen Ordner namens *dummy* in meinem Arbeitsverzeichnis. Ich kann jetzt `cd` in den Ordner und mir den Inhalt anschauen:
+
+```bash
+cd dummy && ls
+```
+
+```bash
+README.md
+```
+
+Wenn du eben einen Repository mit der Hugging Face Hubs Methode `create_repo` erstellt hast, dann sollte dieser Ordner nur eine versteckte `.gitattributes` Datei enthalten. Wenn du es nach den Anweisungen in dem vorherigen Abschnitt mittels der Webinterface gemacht hast, dann sollte der Ordner eine einzige README.md Datei neben der `.gitattributes` enthalten – so wie hier angezeigt wird.
+
+Das Hinzufügen einer Datei mit normaler Größe, z.B. Konfiguration- oder Vokabulardatei, wird so gemach wie in einem git-basierten System. Aber größere Dateien müssen mit git-lfs registriert werden, um sie zu *huggingface.co* zu pushen.
+
+Lass uns kurz zurück zu Python, um ein Modell und einen Tokenizer zu generieren, die wir zu unserem dummy repository committen möchten:
+
+{#if fw === 'pt'}
+```py
+from transformers import AutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = AutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+# Mach was du möchtest mit dem Modell, z.B. trainieren, fine-tunen.
+
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+{:else}
+```py
+from transformers import TFAutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+# Mach was du möchtest mit dem Modell, z.B. trainieren, fine-tunen.
+
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+{/if}
+
+Jetzt haben wir die Modell- und Tokenizer-Artifakte gespeichert und können wir uns nochmal den *dummy* Ordner anschauen:
+
+```bash
+ls
+```
+
+{#if fw === 'pt'}
+```bash
+config.json pytorch_model.bin README.md sentencepiece.bpe.model special_tokens_map.json tokenizer_config.json tokenizer.json
+```
+
+Wenn du dir die Dateigrößen anschaust (z.B. mit `ls -lh`), solltest du sehen, dass die Modell-Statedict Datei (*pytorch_model.bin*) der einzige Ausreißer ist mit über 400 MB.
+
+{:else}
+```bash
+config.json README.md sentencepiece.bpe.model special_tokens_map.json tf_model.h5 tokenizer_config.json tokenizer.json
+```
+
+Wenn du dir die Dateigrößen anschaust (z.B. mit `ls -lh`), solltest du sehen, dass die Modell-Statedict Datei (*t5_model.h5*) der einzige Ausreißer ist mit über 400 MB.
+
+{/if}
+
+> [!TIP]
+> ✏️ Wenn ein Repository mittels der Webinterface kreiert wird, wird die *.gitattributes* Datei automatisch gesetzt, um bestimmte Dateiendungen wie *.bin* und *.h5* als große Dateien zu betrachten, sodass git-lfs sie tracken kann, ohne dass du weiteres konfigurieren musst.
+
+Nun können wir weitermachen und so arbeiten wie wir es mit normalen Git Repositories machen. Wir können die Dateien stagen mit dem Git-Befehl `git add`:
+
+```bash
+git add .
+```
+
+Jetzt schauen wir, welche Dateien gestaged wurden:
+
+```bash
+git status
+```
+
+{#if fw === 'pt'}
+```bash
+On branch main
+Your branch is up to date with 'origin/main'.
+
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: .gitattributes
+ new file: config.json
+ new file: pytorch_model.bin
+ new file: sentencepiece.bpe.model
+ new file: special_tokens_map.json
+ new file: tokenizer.json
+ new file: tokenizer_config.json
+```
+{:else}
+```bash
+On branch main
+Your branch is up to date with 'origin/main'.
+
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: .gitattributes
+ new file: config.json
+ new file: sentencepiece.bpe.model
+ new file: special_tokens_map.json
+ new file: tf_model.h5
+ new file: tokenizer.json
+ new file: tokenizer_config.json
+```
+{/if}
+
+Ähnlicherweise können wir sicherstellen, dass git-lfs die richtigen Dateien trackt mit dem `status` Befehl:
+
+```bash
+git lfs status
+```
+
+{#if fw === 'pt'}
+```bash
+On branch main
+Objects to be pushed to origin/main:
+
+
+Objects to be committed:
+
+ config.json (Git: bc20ff2)
+ pytorch_model.bin (LFS: 35686c2)
+ sentencepiece.bpe.model (LFS: 988bc5a)
+ special_tokens_map.json (Git: cb23931)
+ tokenizer.json (Git: 851ff3e)
+ tokenizer_config.json (Git: f0f7783)
+
+Objects not staged for commit:
+
+
+```
+
+Da sehen wir, dass alle Dateien `Git` als Handler haben. Nur die *pytorch_model.bin* und *sentencepiece.bpe.model* Dateien haben `LFS`. Toll!
+
+{:else}
+```bash
+On branch main
+Objects to be pushed to origin/main:
+
+
+Objects to be committed:
+
+ config.json (Git: bc20ff2)
+ sentencepiece.bpe.model (LFS: 988bc5a)
+ special_tokens_map.json (Git: cb23931)
+ tf_model.h5 (LFS: 86fce29)
+ tokenizer.json (Git: 851ff3e)
+ tokenizer_config.json (Git: f0f7783)
+
+Objects not staged for commit:
+
+
+```
+
+Da sehen wir, dass alle Dateien `Git` als Handler haben. Nur die *t5_model.h5* hat `LFS`. Sehr gut!
+
+{/if}
+
+Lass uns mit den letzten Schritten weitermachen, indem wir die Änderungen commiten und zum *huggingface.co* Remote-Repository pushen:
+
+```bash
+git commit -m "First model version"
+```
+
+{#if fw === 'pt'}
+```bash
+[main b08aab1] First model version
+ 7 files changed, 29027 insertions(+)
+ 6 files changed, 36 insertions(+)
+ create mode 100644 config.json
+ create mode 100644 pytorch_model.bin
+ create mode 100644 sentencepiece.bpe.model
+ create mode 100644 special_tokens_map.json
+ create mode 100644 tokenizer.json
+ create mode 100644 tokenizer_config.json
+```
+{:else}
+```bash
+[main b08aab1] First model version
+ 6 files changed, 36 insertions(+)
+ create mode 100644 config.json
+ create mode 100644 sentencepiece.bpe.model
+ create mode 100644 special_tokens_map.json
+ create mode 100644 tf_model.h5
+ create mode 100644 tokenizer.json
+ create mode 100644 tokenizer_config.json
+```
+{/if}
+
+Das Pushen kann ein bisschen dauern, je nach dem wie schnell deine Internetverbindung ist und wie groß deine Dateien sind:
+
+```bash
+git push
+```
+
+```bash
+Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done.
+Enumerating objects: 11, done.
+Counting objects: 100% (11/11), done.
+Delta compression using up to 12 threads
+Compressing objects: 100% (9/9), done.
+Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done.
+Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
+To https://huggingface.co/lysandre/dummy
+ 891b41d..b08aab1 main -> main
+```
+
+{#if fw === 'pt'}
+Wenn alles durch ist, können wir uns den Repository anschauen und die eben hinzugefügten Dateien finden:
+
+
+

+
+
+Mit der UI kannst du die Modell-Dateien und die Commits explorieren, um die Differenz bei jedem Commit zu sehen:
+
+
+

+
+{:else}
+
+Wenn alles durch ist, können wir uns den Repository anschauen und die eben hinzugefügten Dateien finden:
+
+
+

+
+
+Mit der UI kannst du die Modell-Dateien und die Commits explorieren, um die Differenz bei jedem Commit zu sehen:
+
+
+

+
+{/if}
diff --git a/chapters/de/chapter4/4.mdx b/chapters/de/chapter4/4.mdx
index 1689f5ade..8cd0a65eb 100644
--- a/chapters/de/chapter4/4.mdx
+++ b/chapters/de/chapter4/4.mdx
@@ -1,91 +1,91 @@
-# Erstellung einer Modellkarte ("model card")
-
-
-
-Die Modellkarte (Steckbrief) ist eine Datei, die wahrscheinlich genauso wichtig wie das Modell und der Tokenizer in dem Modell-Repository ist.
-Da liegt die zentrale Definition vom Modell und sie trägt dazu bei, dass andere Menschen der Community das wiederverwenden und die Ergebnisse reproduzieren können – also das ist die Basis, auf der Andere ihre Artifakte bauen können.
-
-Die Dokumentation zum Modell- Training und Evaluierung hilft anderen Nutzer:innen zu verstehen, was sie vom Modell erwarten sollten.
-Ausreichende Information zu der Vor- und Nachaufarbeitung der Daten dient auch dazu, dass man die Einschränkungen, Biases und den Kontext identifizieren kann, wann das Modell nützlich ist und wann nicht.
-
-Deswegen ist die Erstellung einer klar definierten Modellkarte ein sehr wichtiger Schritt. Hier geben wir ein Paar Hinweise, die dir dabei helfen könnten. Die Modellkarte wird durch eine *README.md* Datei (eine Markdown Datei) kreiert, die du schonmal gesehen hast.
-
-Das Konzept von Modellkarte ("model card") stammt aus einer Forschungsrichtung bei Google, die zuerst in dem Paper ["Model Cards for Model Reporting"](https://arxiv.org/abs/1810.03993) von Margaret Mitchell et al erschien. Vieles von dem, was hier steht, basiert auf dem Paper und wir empfehlen dir, das Paper zu lesen, um besser zu verstehen, warum Modellkarten so wichtig sind, wenn man Wert auf Reproduzierbarkeit, Wiederverwendbarkeit und Fairness legt.
-
-Eine Modellkarte fängt mit einer kurzen, große Übersicht davon, was das Modell kann plus einige Details in den folgenden Abschnitte:
-
-- Modell-Beschreibung
-- Beabsichtigte Nutzung und Einschränkungen
-- Modell-Bedienung
-- Einschränkungen und Bias
-- Trainingsdaten
-- Trainingsverfahren
-- Evaluierungsergebnisse
-
-Lass uns anschauen, was genau in jedem Abschnitt stehen sollte.
-
-### Modell-Beschreibung
-
-Die Modellbeschreibung enthält grundlegende Details zum Modell. Dazu gehören die Architektur, die Version, ob es in einem Paper vorgestellt wurde, ob eine Originalimplementierung verfügbar ist, der Autor und allgemeine Informationen über das Modell. Eventuelle Urheberrechte sind hier anzugeben. In diesem Abschnitt können auch allgemeine Informationen zu Trainingsverfahren, Parametern und wichtigen Haftungsausschlüssen erwähnt werden.
-
-### Verwendungszweck und Einschränkungen
-
-Hier beschreibst du die angedachten Anwendungsfälle fürs Modell, einschließlich der Sprachen, Felder und Domänen, in denen es angewendet werden kann. In diesem Abschnitt der Modellkarte können auch Bereiche dokumentiert werden, die bekanntermaßen außerhalb des Anwendungsbereichs des Modells liegen oder in denen die Leistung wahrscheinlich nicht optimal ist.
-
-### Modell-Bedienung
-
-Dieser Abschnitt sollte einige Beispiele für die Verwendung des Modells enthalten. Dies kann die Verwendung der Funktion `pipeline()`, die Verwendung der Modell- und Tokenizer-Klassen und jeden anderen Code zeigen, der deiner Meinung nach hilfreich sein könnte.
-
-
-### Trainingsdaten
-
-In diesem Teil sollte angegeben werden, auf welchen Datensatz bzw. Datensätze das Modell trainiert wurde. Eine kurze Beschreibung des Datensatzes/der Datensätze ist ebenfalls willkommen.
-
-### Trainingsverfahren
-
-In diesem Abschnitt solltest du alle relevanten Aspekte des Modelltrainingsverfahren beschreiben, die für die Reproduzierbarkeit nützlich sind. Dazu gehören alle Vor- und Nachbearbeitungen, die an den Daten durchgeführt wurden, sowie Details wie die Anzahl der Trainingsepochene, Batch-Größe, die Lernrate usw.
-
-### Variablen und Metriken
-
-Hier solltest du die Bewertungsmetriken beschreiben und die verschiedenen Faktoren, die du dabei mit berücksichtigst. Durch die Angabe, welche Metrik(en) verwendet wurden, für welchen Datensatz und welche Datensatzaufteilung, kannst du die Leistung deines Modells leicht mit der anderer Modelle vergleichen. Diese sollten durch die vorherigen Abschnitte informiert werden, wie z. B. die beabsichtigten Benutzer und Anwendungsfälle.
-
-### Evaluierungsergebnisse
-
-Abschließend gibst du an, wie gut das Modell mit dem Bewertungsdatensatz abschneidet. Wenn das Modell einen Entscheidungsschwellenwert verwendet, gib entweder den in der Bewertung verwendeten Entscheidungsschwellenwert an oder mach Angaben zur Bewertung bei verschiedenen Schwellenwerten für die beabsichtigten Verwendungszwecke.
-
-## Beispiel
-
-Im Folgenden findest du einige Beispiele von guten Modellkarten:
-
-- [`bert-base-cased`](https://huggingface.co/bert-base-cased)
-- [`gpt2`](https://huggingface.co/gpt2)
-- [`distilbert`](https://huggingface.co/distilbert-base-uncased)
-
-
-Mehr Beispiele von verschiedene Organisationen/Firmen sind hier verfügbar [here](https://github.com/huggingface/model_card/blob/master/examples.md).
-
-## Hinweis
-
-Modellkarten sind bei der Veröffentlichung von Modellen nicht erforderlich und du musst bei der Erstellung nicht alle oben beschriebenen Abschnitte einbeziehen. Allerdings kann eine explizite Dokumentation des Modells künftigen Nutzern nur nützen, daher empfehlen wir dir, so viele Abschnitte wie möglich nach bestem Wissen und Gewissen auszufüllen.
-
-## Modellkarte-Metadaten
-
-Wenn du den Hugging Face Hub ein wenig erkundet hast, solltest du gesehen haben, dass einige Modelle zu bestimmten Kategorien gehören: Du kannst sie nach Aufgaben, Sprachen, Bibliotheken und mehr filtern. Die Kategorien, zu denen ein Modell gehört, werden anhand der Metadaten identifiziert, die du im Kopf der Modellkarte hinzufügst.
-
-Zum Beispiel sieh dir dieses an [`camembert-base` model card](https://huggingface.co/camembert-base/blob/main/README.md). Du solltest folgende Zeilen auf der Modellkarte sehen:
-
-```
----
-language: fr
-license: mit
-datasets:
-- oscar
----
-```
-
-Diese Metadaten werden vom Hugging Face Hub analysiert, der dieses Modell dann als französisches Modell mit einer MIT-Lizenz identifiziert, das auf dem Oscar-Datensatz trainiert wurde.
-
-Die vollständige [Modellkarte](https://github.com/huggingface/hub-docs/blame/main/modelcard.md) ermöglicht die Angabe von Sprachen, Lizenzen, Tags, Datensätzen, Metriken sowie den Bewertungsergebnissen, die das Modell wann erhalten hat Ausbildung.
+# Erstellung einer Modellkarte ("model card")
+
+
+
+Die Modellkarte (Steckbrief) ist eine Datei, die wahrscheinlich genauso wichtig wie das Modell und der Tokenizer in dem Modell-Repository ist.
+Da liegt die zentrale Definition vom Modell und sie trägt dazu bei, dass andere Menschen der Community das wiederverwenden und die Ergebnisse reproduzieren können – also das ist die Basis, auf der Andere ihre Artifakte bauen können.
+
+Die Dokumentation zum Modell- Training und Evaluierung hilft anderen Nutzer:innen zu verstehen, was sie vom Modell erwarten sollten.
+Ausreichende Information zu der Vor- und Nachaufarbeitung der Daten dient auch dazu, dass man die Einschränkungen, Biases und den Kontext identifizieren kann, wann das Modell nützlich ist und wann nicht.
+
+Deswegen ist die Erstellung einer klar definierten Modellkarte ein sehr wichtiger Schritt. Hier geben wir ein Paar Hinweise, die dir dabei helfen könnten. Die Modellkarte wird durch eine *README.md* Datei (eine Markdown Datei) kreiert, die du schonmal gesehen hast.
+
+Das Konzept von Modellkarte ("model card") stammt aus einer Forschungsrichtung bei Google, die zuerst in dem Paper ["Model Cards for Model Reporting"](https://arxiv.org/abs/1810.03993) von Margaret Mitchell et al erschien. Vieles von dem, was hier steht, basiert auf dem Paper und wir empfehlen dir, das Paper zu lesen, um besser zu verstehen, warum Modellkarten so wichtig sind, wenn man Wert auf Reproduzierbarkeit, Wiederverwendbarkeit und Fairness legt.
+
+Eine Modellkarte fängt mit einer kurzen, große Übersicht davon, was das Modell kann plus einige Details in den folgenden Abschnitte:
+
+- Modell-Beschreibung
+- Beabsichtigte Nutzung und Einschränkungen
+- Modell-Bedienung
+- Einschränkungen und Bias
+- Trainingsdaten
+- Trainingsverfahren
+- Evaluierungsergebnisse
+
+Lass uns anschauen, was genau in jedem Abschnitt stehen sollte.
+
+### Modell-Beschreibung
+
+Die Modellbeschreibung enthält grundlegende Details zum Modell. Dazu gehören die Architektur, die Version, ob es in einem Paper vorgestellt wurde, ob eine Originalimplementierung verfügbar ist, der Autor und allgemeine Informationen über das Modell. Eventuelle Urheberrechte sind hier anzugeben. In diesem Abschnitt können auch allgemeine Informationen zu Trainingsverfahren, Parametern und wichtigen Haftungsausschlüssen erwähnt werden.
+
+### Verwendungszweck und Einschränkungen
+
+Hier beschreibst du die angedachten Anwendungsfälle fürs Modell, einschließlich der Sprachen, Felder und Domänen, in denen es angewendet werden kann. In diesem Abschnitt der Modellkarte können auch Bereiche dokumentiert werden, die bekanntermaßen außerhalb des Anwendungsbereichs des Modells liegen oder in denen die Leistung wahrscheinlich nicht optimal ist.
+
+### Modell-Bedienung
+
+Dieser Abschnitt sollte einige Beispiele für die Verwendung des Modells enthalten. Dies kann die Verwendung der Funktion `pipeline()`, die Verwendung der Modell- und Tokenizer-Klassen und jeden anderen Code zeigen, der deiner Meinung nach hilfreich sein könnte.
+
+
+### Trainingsdaten
+
+In diesem Teil sollte angegeben werden, auf welchen Datensatz bzw. Datensätze das Modell trainiert wurde. Eine kurze Beschreibung des Datensatzes/der Datensätze ist ebenfalls willkommen.
+
+### Trainingsverfahren
+
+In diesem Abschnitt solltest du alle relevanten Aspekte des Modelltrainingsverfahren beschreiben, die für die Reproduzierbarkeit nützlich sind. Dazu gehören alle Vor- und Nachbearbeitungen, die an den Daten durchgeführt wurden, sowie Details wie die Anzahl der Trainingsepochene, Batch-Größe, die Lernrate usw.
+
+### Variablen und Metriken
+
+Hier solltest du die Bewertungsmetriken beschreiben und die verschiedenen Faktoren, die du dabei mit berücksichtigst. Durch die Angabe, welche Metrik(en) verwendet wurden, für welchen Datensatz und welche Datensatzaufteilung, kannst du die Leistung deines Modells leicht mit der anderer Modelle vergleichen. Diese sollten durch die vorherigen Abschnitte informiert werden, wie z. B. die beabsichtigten Benutzer und Anwendungsfälle.
+
+### Evaluierungsergebnisse
+
+Abschließend gibst du an, wie gut das Modell mit dem Bewertungsdatensatz abschneidet. Wenn das Modell einen Entscheidungsschwellenwert verwendet, gib entweder den in der Bewertung verwendeten Entscheidungsschwellenwert an oder mach Angaben zur Bewertung bei verschiedenen Schwellenwerten für die beabsichtigten Verwendungszwecke.
+
+## Beispiel
+
+Im Folgenden findest du einige Beispiele von guten Modellkarten:
+
+- [`bert-base-cased`](https://huggingface.co/bert-base-cased)
+- [`gpt2`](https://huggingface.co/gpt2)
+- [`distilbert`](https://huggingface.co/distilbert-base-uncased)
+
+
+Mehr Beispiele von verschiedene Organisationen/Firmen sind hier verfügbar [here](https://github.com/huggingface/model_card/blob/master/examples.md).
+
+## Hinweis
+
+Modellkarten sind bei der Veröffentlichung von Modellen nicht erforderlich und du musst bei der Erstellung nicht alle oben beschriebenen Abschnitte einbeziehen. Allerdings kann eine explizite Dokumentation des Modells künftigen Nutzern nur nützen, daher empfehlen wir dir, so viele Abschnitte wie möglich nach bestem Wissen und Gewissen auszufüllen.
+
+## Modellkarte-Metadaten
+
+Wenn du den Hugging Face Hub ein wenig erkundet hast, solltest du gesehen haben, dass einige Modelle zu bestimmten Kategorien gehören: Du kannst sie nach Aufgaben, Sprachen, Bibliotheken und mehr filtern. Die Kategorien, zu denen ein Modell gehört, werden anhand der Metadaten identifiziert, die du im Kopf der Modellkarte hinzufügst.
+
+Zum Beispiel sieh dir dieses an [`camembert-base` model card](https://huggingface.co/camembert-base/blob/main/README.md). Du solltest folgende Zeilen auf der Modellkarte sehen:
+
+```
+---
+language: fr
+license: mit
+datasets:
+- oscar
+---
+```
+
+Diese Metadaten werden vom Hugging Face Hub analysiert, der dieses Modell dann als französisches Modell mit einer MIT-Lizenz identifiziert, das auf dem Oscar-Datensatz trainiert wurde.
+
+Die vollständige [Modellkarte](https://github.com/huggingface/hub-docs/blame/main/modelcard.md) ermöglicht die Angabe von Sprachen, Lizenzen, Tags, Datensätzen, Metriken sowie den Bewertungsergebnissen, die das Modell wann erhalten hat Ausbildung.
diff --git a/chapters/de/chapter4/5.mdx b/chapters/de/chapter4/5.mdx
index 65eac0fa9..e1feb9b6d 100644
--- a/chapters/de/chapter4/5.mdx
+++ b/chapters/de/chapter4/5.mdx
@@ -1,12 +1,12 @@
-# Teil 1 abgeschlossen!
-
-
-
-Dies ist das Ende des ersten Teils des Kurses! Teil 2 wird am 15. November mit einem großen Community-Event veröffentlicht, weitere Informationen findest du [hier](https://huggingface.co/blog/course-launch-event).
-
-Du solltest nun in der Lage sein, ein vorab trainiertes Modell für ein Textklassifizierungsproblem (einzelne Sätze oder Satzpaare) zu optimieren und das Ergebnis in den Model Hub hochzuladen. Um sicherzustellen, dass du diesen ersten Abschnitt beherrschst, solltest du genau das an einem Problem verwenden, das dich interessiert (und nicht unbedingt auf Englisch, wenn Sie eine andere Sprache sprechen)! Hilfe findest du in den [Hugging Face-Foren](https://discuss.huggingface.co/) und du kannst dein Projekt in [diesem Thema](https://discuss.huggingface.co/t/share-your-projects) teilen /6803), sobald du damit fertig bist.
-
-Wir freuen uns darauf, zu sehen, was du alles damit baust!
+# Teil 1 abgeschlossen!
+
+
+
+Dies ist das Ende des ersten Teils des Kurses! Teil 2 wird am 15. November mit einem großen Community-Event veröffentlicht, weitere Informationen findest du [hier](https://huggingface.co/blog/course-launch-event).
+
+Du solltest nun in der Lage sein, ein vorab trainiertes Modell für ein Textklassifizierungsproblem (einzelne Sätze oder Satzpaare) zu optimieren und das Ergebnis in den Model Hub hochzuladen. Um sicherzustellen, dass du diesen ersten Abschnitt beherrschst, solltest du genau das an einem Problem verwenden, das dich interessiert (und nicht unbedingt auf Englisch, wenn Sie eine andere Sprache sprechen)! Hilfe findest du in den [Hugging Face-Foren](https://discuss.huggingface.co/) und du kannst dein Projekt in [diesem Thema](https://discuss.huggingface.co/t/share-your-projects) teilen /6803), sobald du damit fertig bist.
+
+Wir freuen uns darauf, zu sehen, was du alles damit baust!
diff --git a/chapters/de/chapter4/6.mdx b/chapters/de/chapter4/6.mdx
index cc87f7cb3..4577653d9 100644
--- a/chapters/de/chapter4/6.mdx
+++ b/chapters/de/chapter4/6.mdx
@@ -1,231 +1,231 @@
-
-
-
-
-# Quiz am Ende des Kapitels
-
-
-
-Lass uns testen, was du im vorheringen Kapitel gelernt hast!
-
-### 1. Auf welche Modelle sind die Hub-Modelle beschränkt?
-
-
-
-### 2. Wie kannst du die Modelle auf dem Hub verwalten?
-
-git-lfs benutzen.",
- correct: true
- }
- ]}
-/>
-
-### 3. Was kannst du mit der Hugging Face Hub-Weboberfläche tun?
-
-
-
-### 4. Was ist eine Modellkarte?
-
-
-
-### 5. Welche dieser Objekte der 🤗 Transformers-Bibliothek können mit `push_to_hub()` direkt auf dem Hub geteilt werden?
-
-{#if fw === 'pt'}
-push_to_hub, und wenn su sie verwendest, werden alle Tokenizer-Dateien (Vokabular, Architektur des Tokenizers usw.) in ein bestimmtes Repo verschoben. Aber das ist nicht die einzig richtige Antwort!",
- correct: true
- },
- {
- text: "Eine Modell-Konfiguration",
- explain: "Richtig! Alle Modellkonfigurationen verfügen über die Methode push_to_hub, und wenn Sie sie verwenden, werden sie an ein bestimmtes Repo gepusht. Was kannst du sonst noch teilen?",
- correct: true
- },
- {
- text: "Ein Model",
- explain: "Richtig! Alle Modelle verfügen über die Methode push_to_hub, und wenn du sie verwendest, werden sie und ihre Konfigurationsdateien in ein bestimmtes Repo gepusht. Das ist jedoch nicht alles, was du teilen kannst.",
- correct: true
- },
- {
- text: "Ein Trainer",
- explain: "Das ist richtig – der Trainer implementiert auch die Methode push_to_hub und lädt mit dieser Methode das Modell, seine Konfiguration, den Tokenizer und einen Modellkartenentwurf auf einen gegebenen Server hoch repo. Versuch es auch mit einer anderen Antwort!",
- correct: true
- }
- ]}
-/>
-{:else}
-push_to_hub method, and using it will push all the tokenizer files (vocabulary, architecture of the tokenizer, etc.) to a given repo. That's not the only right answer, though!",
- explain: "Richtig! Alle Tokenizer verfügen über die Methode push_to_hub, und wenn du sie verwendest, werden alle Tokenizer-Dateien (Vokabular, Architektur des Tokenizers usw.) in ein bestimmtes Repo verschoben. Das ist aber nicht die einzige richtige Antwort!",
- correct: true
- },
- {
- text: "Eine Modell-Konfiguration",
- explain: "Right! All model configurations have the push_to_hub method, and using it will push them to a given repo. What else can you share?",
- explain: "Richtig! Alle Modellkonfigurationen verfügen über die Methode push_to_hub, und wenn du sie verwendest, werden sie an ein bestimmtes Repo gepusht. Was kannst du sonst noch teilen?",
- correct: true
- },
- {
- text: "Ein Modell",
- explain: "Richtig! Alle Modelle verfügen über die Methode push_to_hub, und wenn du sie verwendest, werden sie und ihre Konfigurationsdateien in ein bestimmtes Repo gepusht. Das ist jedoch nicht alles, was du teilen kannst.",
- correct: true
- },
- {
- text: "Alles oben mit einem speziellen `Callback`",
- explain: "Das ist richtig – der PushToHubCallback sendet während des Trainings regelmäßig alle diese Objekte an ein Repo.",
- correct: true
- }
- ]}
-/>
-{/if}
-
-### 6. Was ist der erste Schritt bei Verwendung der Methode `push_to_hub()` oder der CLI-Tools?
-
-
-
-### 7. Du verwendest ein Modell und einen Tokenizer – wie kannst du diese auf den Hub hochladen?
-
-huggingface_hub-Dienstprogramm einschließt.",
- explain: "Modelle und Tokenizer profitieren bereits von den Dienstprogrammen huggingface_hub: kein zusätzlicher Wrapping erforderlich!"
- },
- {
- text: "Indem du sie auf der Festplatte speicherst und transformers-cli upload-model aufrufst",
- explain: "Der Befehl upload-model existiert nicht."
- }
- ]}
-/>
-
-### 8. Welche Git-Operationen kann man mit der Klasse „Repository“ ausführen?
-
-git_commit() Methode da.",
- correct: true
- },
- {
- text: "Ein Pull",
- explain: "Das ist der Zweck der git_pull() Methode.",
- correct: true
- },
- {
- text: "Ein Push",
- explain: "Die Methode git_push() macht das.",
- correct: true
- },
- {
- text: "Ein Merge",
- explain: "Nein, die Operation wird mit dieser API nie möglich sein."
- }
- ]}
-/>
+
+
+
+
+# Quiz am Ende des Kapitels
+
+
+
+Lass uns testen, was du im vorheringen Kapitel gelernt hast!
+
+### 1. Auf welche Modelle sind die Hub-Modelle beschränkt?
+
+
+
+### 2. Wie kannst du die Modelle auf dem Hub verwalten?
+
+git-lfs benutzen.",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. Was kannst du mit der Hugging Face Hub-Weboberfläche tun?
+
+
+
+### 4. Was ist eine Modellkarte?
+
+
+
+### 5. Welche dieser Objekte der 🤗 Transformers-Bibliothek können mit `push_to_hub()` direkt auf dem Hub geteilt werden?
+
+{#if fw === 'pt'}
+push_to_hub, und wenn su sie verwendest, werden alle Tokenizer-Dateien (Vokabular, Architektur des Tokenizers usw.) in ein bestimmtes Repo verschoben. Aber das ist nicht die einzig richtige Antwort!",
+ correct: true
+ },
+ {
+ text: "Eine Modell-Konfiguration",
+ explain: "Richtig! Alle Modellkonfigurationen verfügen über die Methode push_to_hub, und wenn Sie sie verwenden, werden sie an ein bestimmtes Repo gepusht. Was kannst du sonst noch teilen?",
+ correct: true
+ },
+ {
+ text: "Ein Model",
+ explain: "Richtig! Alle Modelle verfügen über die Methode push_to_hub, und wenn du sie verwendest, werden sie und ihre Konfigurationsdateien in ein bestimmtes Repo gepusht. Das ist jedoch nicht alles, was du teilen kannst.",
+ correct: true
+ },
+ {
+ text: "Ein Trainer",
+ explain: "Das ist richtig – der Trainer implementiert auch die Methode push_to_hub und lädt mit dieser Methode das Modell, seine Konfiguration, den Tokenizer und einen Modellkartenentwurf auf einen gegebenen Server hoch repo. Versuch es auch mit einer anderen Antwort!",
+ correct: true
+ }
+ ]}
+/>
+{:else}
+push_to_hub method, and using it will push all the tokenizer files (vocabulary, architecture of the tokenizer, etc.) to a given repo. That's not the only right answer, though!",
+ explain: "Richtig! Alle Tokenizer verfügen über die Methode push_to_hub, und wenn du sie verwendest, werden alle Tokenizer-Dateien (Vokabular, Architektur des Tokenizers usw.) in ein bestimmtes Repo verschoben. Das ist aber nicht die einzige richtige Antwort!",
+ correct: true
+ },
+ {
+ text: "Eine Modell-Konfiguration",
+ explain: "Right! All model configurations have the push_to_hub method, and using it will push them to a given repo. What else can you share?",
+ explain: "Richtig! Alle Modellkonfigurationen verfügen über die Methode push_to_hub, und wenn du sie verwendest, werden sie an ein bestimmtes Repo gepusht. Was kannst du sonst noch teilen?",
+ correct: true
+ },
+ {
+ text: "Ein Modell",
+ explain: "Richtig! Alle Modelle verfügen über die Methode push_to_hub, und wenn du sie verwendest, werden sie und ihre Konfigurationsdateien in ein bestimmtes Repo gepusht. Das ist jedoch nicht alles, was du teilen kannst.",
+ correct: true
+ },
+ {
+ text: "Alles oben mit einem speziellen `Callback`",
+ explain: "Das ist richtig – der PushToHubCallback sendet während des Trainings regelmäßig alle diese Objekte an ein Repo.",
+ correct: true
+ }
+ ]}
+/>
+{/if}
+
+### 6. Was ist der erste Schritt bei Verwendung der Methode `push_to_hub()` oder der CLI-Tools?
+
+
+
+### 7. Du verwendest ein Modell und einen Tokenizer – wie kannst du diese auf den Hub hochladen?
+
+huggingface_hub-Dienstprogramm einschließt.",
+ explain: "Modelle und Tokenizer profitieren bereits von den Dienstprogrammen huggingface_hub: kein zusätzlicher Wrapping erforderlich!"
+ },
+ {
+ text: "Indem du sie auf der Festplatte speicherst und transformers-cli upload-model aufrufst",
+ explain: "Der Befehl upload-model existiert nicht."
+ }
+ ]}
+/>
+
+### 8. Welche Git-Operationen kann man mit der Klasse „Repository“ ausführen?
+
+git_commit() Methode da.",
+ correct: true
+ },
+ {
+ text: "Ein Pull",
+ explain: "Das ist der Zweck der git_pull() Methode.",
+ correct: true
+ },
+ {
+ text: "Ein Push",
+ explain: "Die Methode git_push() macht das.",
+ correct: true
+ },
+ {
+ text: "Ein Merge",
+ explain: "Nein, die Operation wird mit dieser API nie möglich sein."
+ }
+ ]}
+/>
diff --git a/chapters/de/glossary/1.mdx b/chapters/de/glossary/1.mdx
index 82c7d5fbd..bd3019a41 100644
--- a/chapters/de/glossary/1.mdx
+++ b/chapters/de/glossary/1.mdx
@@ -1,141 +1,141 @@
-# Wörterverzeichnis
-
-| Original | Übersetzung |
-|---------------------------------|-----------------------------------------|
-| Abstraction | Abstraktion |
-| Account | Account |
-| Accuracy | Genauigkeit |
-| Artificial General Intelligence | künstliche allgemeine Intelligenz |
-| Attention | Attention |
-| Attention mask (layer) | Attention-Mask (Layer) |
-| Backward Pass | Rückwärtsalgorithmus berechnen |
-| Batch | Batch |
-| Bias | Bias (Voreingenommenheit) |
-| Causal Language Modeling | kausale Sprachmodellierung |
-| Chapter | Kapitel |
-| Checkpoint(s) | Checkpoint(s) |
-| Class | Klasse |
-| Classification | Klassifizierung |
-| Code | Code |
-| Colab Notebook | Colab Notebook |
-| Command | Befehl |
-| Computer Vision | Computer Vision |
-| Configuration | Konfiguration |
-| Course | Kurs |
-| Decoder | Decoder |
-| Dependency | Abhängigkeitsbeziehung |
-| Deployment | Deployment |
-| Development | Entwicklung |
-| Dictionary | Dictionary |
-| Distribution | Verteilung |
-| Download | Download |
-| Encoder | Encoder |
-| Extractive question answering | Extraktives Question Answering |
-| F1 score | F1-Maß |
-| Feature | Feature |
-| Fine-tune | feintunen |
-| Fine-tuning | Feintuning |
-| Folder | Ordner |
-| Forward Pass | Vorwärtsalgorithmus berechnen |
-| Function | Funktion |
-| Generative question answering | Generatives Question Answering |
-| Google | Google |
-| Hugging Face | Hugging Face |
-| Incompatibility | Inkompatibilität |
-| Inference | Inferenz |
-| Input | Input |
-| Input data | Input-Daten |
-| Label (verb) | labeln (gelabelt), annotieren |
-| Label (subj) | Label, das / Labels, die (plur.) |
-| Layer | Layer (plur. Layer(n)) |
-| Library | Bibliothek |
-| Linux | Linux |
-| Load | laden |
-| Loss function | Verlustfunktion |
-| Machine Learning | Maschinelles Lernen |
-| macOS | macOS |
-| Mask | Maskierung |
-| Mask Filling | Mask Filling |
-| Mask Token | Mask-Token |
-| Masked Language Modeling | maskierte Sprachmodellierung |
-| Model | Modell |
-| Model Hub | Model Hub |
-| Module | Modul |
-| Named Entities | benannte Entitäten |
-| Named Entity Recognition | Eigennamenerkennung |
-| Natural Language Processing | Computerlinguistik |
-| Output | Output |
-| Package | Paket |
-| Package Manager | Paketverwaltung |
-| Padding | das Padding / auffüllen |
-| Parameter | Parameter |
-| Postprocessing | Nachverarveitung |
-| Preprocessing | Vorverarbeitung |
-| Pretraining | Pretraining |
-| Pretrained model | vortrainiertes Modell |
-| Prompt | Prompt |
-| Python | Python |
-| Pytorch | Pytorch |
-| Question Answering | Question Answering |
-| Save | speichern |
-| Sample | Sample (auch Stichprobe) |
-| Script | Script |
-| Self-Contained | in sich abgeschlossen |
-| Sentiment analysis | Sentiment-Analyse |
-| Sequence-to-sequence models | Sequence-to-Sequence-Modelle |
-| Setup | Installation |
-| Speech Processing | Verarbeitung gesprochener Sprache |
-| Speech Recognition | Spracherkennung |
-| Summarization | Automatische Textzusammenfassung |
-| Target | Zielvariable / vorherzusagende Variable |
-| Task | Aufgabe / Aufgabenstellung |
-| TensorFlow | Tensorflow |
-| Terminal | Terminal |
-| Text generation | Textgenerierung |
-| Tokenizer | Tokenizer |
-| Train | Training |
-| Transfer Learning | Transfer Learning |
-| Transformer | Transformer |
-| Transformer models | Transformer-Modelle |
-| Translation | Maschinelle Übersetzung |
-| Virtual Environment | Virtuelle Umgebung |
-| Weight | Gewicht |
-| Weights | Gewichtung |
-| Windows | Windows |
-| Working Environment | Arbeitsumgebung |
-| Workload | Auslastung |
-| Workspace | Workspace |
-| Zero-shot classification | Zero-Shot-Klassifizierung |
-=======
-
-## Abkürzungen
-
-| Original | Übersetzung |
-|-----------|-------------|
-| NLP | CL |
-| API | API |
-| GPU | GPU |
-| TPU | TPU |
-| ML | ML |
-
-## Notes
-
-Please refer to [TRANSLATING.txt](/chapters/de/TRANSLATING.txt) for a translation guide. Here are some excerpts relevant to the glossary:
-
-- Refer and contribute to the glossary frequently to stay on top of the latest
- choices we make. This minimizes the amount of editing that is required.
- Add new terms alphabetically sorted.
-
-- The German language accepts English words especially in modern contexts more
- than many other languages (i.e. Anglicisms). Check for the correct usage of
- terms in computer science and commonly used terms in other publications.
-
-- Don't translate industry-accepted acronyms. e.g. TPU or GPU.
-
-- If translating a technical word, keep the choice of German translation consistent.
- This does not apply for non-technical choices, as in those cases variety actually
- helps keep the text engaging.
-
-- Be exact when choosing equivalents for technical words. Package is package.
- Library is library. Don't mix and match.
-
+# Wörterverzeichnis
+
+| Original | Übersetzung |
+|---------------------------------|-----------------------------------------|
+| Abstraction | Abstraktion |
+| Account | Account |
+| Accuracy | Genauigkeit |
+| Artificial General Intelligence | künstliche allgemeine Intelligenz |
+| Attention | Attention |
+| Attention mask (layer) | Attention-Mask (Layer) |
+| Backward Pass | Rückwärtsalgorithmus berechnen |
+| Batch | Batch |
+| Bias | Bias (Voreingenommenheit) |
+| Causal Language Modeling | kausale Sprachmodellierung |
+| Chapter | Kapitel |
+| Checkpoint(s) | Checkpoint(s) |
+| Class | Klasse |
+| Classification | Klassifizierung |
+| Code | Code |
+| Colab Notebook | Colab Notebook |
+| Command | Befehl |
+| Computer Vision | Computer Vision |
+| Configuration | Konfiguration |
+| Course | Kurs |
+| Decoder | Decoder |
+| Dependency | Abhängigkeitsbeziehung |
+| Deployment | Deployment |
+| Development | Entwicklung |
+| Dictionary | Dictionary |
+| Distribution | Verteilung |
+| Download | Download |
+| Encoder | Encoder |
+| Extractive question answering | Extraktives Question Answering |
+| F1 score | F1-Maß |
+| Feature | Feature |
+| Fine-tune | feintunen |
+| Fine-tuning | Feintuning |
+| Folder | Ordner |
+| Forward Pass | Vorwärtsalgorithmus berechnen |
+| Function | Funktion |
+| Generative question answering | Generatives Question Answering |
+| Google | Google |
+| Hugging Face | Hugging Face |
+| Incompatibility | Inkompatibilität |
+| Inference | Inferenz |
+| Input | Input |
+| Input data | Input-Daten |
+| Label (verb) | labeln (gelabelt), annotieren |
+| Label (subj) | Label, das / Labels, die (plur.) |
+| Layer | Layer (plur. Layer(n)) |
+| Library | Bibliothek |
+| Linux | Linux |
+| Load | laden |
+| Loss function | Verlustfunktion |
+| Machine Learning | Maschinelles Lernen |
+| macOS | macOS |
+| Mask | Maskierung |
+| Mask Filling | Mask Filling |
+| Mask Token | Mask-Token |
+| Masked Language Modeling | maskierte Sprachmodellierung |
+| Model | Modell |
+| Model Hub | Model Hub |
+| Module | Modul |
+| Named Entities | benannte Entitäten |
+| Named Entity Recognition | Eigennamenerkennung |
+| Natural Language Processing | Computerlinguistik |
+| Output | Output |
+| Package | Paket |
+| Package Manager | Paketverwaltung |
+| Padding | das Padding / auffüllen |
+| Parameter | Parameter |
+| Postprocessing | Nachverarveitung |
+| Preprocessing | Vorverarbeitung |
+| Pretraining | Pretraining |
+| Pretrained model | vortrainiertes Modell |
+| Prompt | Prompt |
+| Python | Python |
+| Pytorch | Pytorch |
+| Question Answering | Question Answering |
+| Save | speichern |
+| Sample | Sample (auch Stichprobe) |
+| Script | Script |
+| Self-Contained | in sich abgeschlossen |
+| Sentiment analysis | Sentiment-Analyse |
+| Sequence-to-sequence models | Sequence-to-Sequence-Modelle |
+| Setup | Installation |
+| Speech Processing | Verarbeitung gesprochener Sprache |
+| Speech Recognition | Spracherkennung |
+| Summarization | Automatische Textzusammenfassung |
+| Target | Zielvariable / vorherzusagende Variable |
+| Task | Aufgabe / Aufgabenstellung |
+| TensorFlow | Tensorflow |
+| Terminal | Terminal |
+| Text generation | Textgenerierung |
+| Tokenizer | Tokenizer |
+| Train | Training |
+| Transfer Learning | Transfer Learning |
+| Transformer | Transformer |
+| Transformer models | Transformer-Modelle |
+| Translation | Maschinelle Übersetzung |
+| Virtual Environment | Virtuelle Umgebung |
+| Weight | Gewicht |
+| Weights | Gewichtung |
+| Windows | Windows |
+| Working Environment | Arbeitsumgebung |
+| Workload | Auslastung |
+| Workspace | Workspace |
+| Zero-shot classification | Zero-Shot-Klassifizierung |
+=======
+
+## Abkürzungen
+
+| Original | Übersetzung |
+|-----------|-------------|
+| NLP | CL |
+| API | API |
+| GPU | GPU |
+| TPU | TPU |
+| ML | ML |
+
+## Notes
+
+Please refer to [TRANSLATING.txt](/chapters/de/TRANSLATING.txt) for a translation guide. Here are some excerpts relevant to the glossary:
+
+- Refer and contribute to the glossary frequently to stay on top of the latest
+ choices we make. This minimizes the amount of editing that is required.
+ Add new terms alphabetically sorted.
+
+- The German language accepts English words especially in modern contexts more
+ than many other languages (i.e. Anglicisms). Check for the correct usage of
+ terms in computer science and commonly used terms in other publications.
+
+- Don't translate industry-accepted acronyms. e.g. TPU or GPU.
+
+- If translating a technical word, keep the choice of German translation consistent.
+ This does not apply for non-technical choices, as in those cases variety actually
+ helps keep the text engaging.
+
+- Be exact when choosing equivalents for technical words. Package is package.
+ Library is library. Don't mix and match.
+
diff --git a/chapters/en/_toctree.yml b/chapters/en/_toctree.yml
index e7d22597b..9c05907cb 100644
--- a/chapters/en/_toctree.yml
+++ b/chapters/en/_toctree.yml
@@ -1,264 +1,264 @@
-- title: 0. Setup
- sections:
- - local: chapter0/1
- title: Introduction
-
-- title: 1. Transformer models
- sections:
- - local: chapter1/1
- title: Introduction
- - local: chapter1/2
- title: Natural Language Processing and Large Language Models
- - local: chapter1/3
- title: Transformers, what can they do?
- - local: chapter1/4
- title: How do Transformers work?
- - local: chapter1/5
- title: How 🤗 Transformers solve tasks
- - local: chapter1/6
- title: Transformer Architectures
- - local: chapter1/7
- title: Quick quiz
- - local: chapter1/8
- title: Inference with LLMs
- - local: chapter1/9
- title: Bias and limitations
- - local: chapter1/10
- title: Summary
- - local: chapter1/11
- title: Certification exam
- quiz: 1
-
-- title: 2. Using 🤗 Transformers
- sections:
- - local: chapter2/1
- title: Introduction
- - local: chapter2/2
- title: Behind the pipeline
- - local: chapter2/3
- title: Models
- - local: chapter2/4
- title: Tokenizers
- - local: chapter2/5
- title: Handling multiple sequences
- - local: chapter2/6
- title: Putting it all together
- - local: chapter2/7
- title: Basic usage completed!
- - local: chapter2/8
- title: Optimized Inference Deployment
- - local: chapter2/9
- title: End-of-chapter quiz
- quiz: 2
-
-- title: 3. Fine-tuning a pretrained model
- sections:
- - local: chapter3/1
- title: Introduction
- - local: chapter3/2
- title: Processing the data
- - local: chapter3/3
- title: Fine-tuning a model with the Trainer API
- - local: chapter3/4
- title: A full training loop
- - local: chapter3/5
- title: Understanding Learning Curves
- - local: chapter3/6
- title: Fine-tuning, Check!
- - local: chapter3/7
- title: End-of-chapter quiz
- quiz: 3
-
-- title: 4. Sharing models and tokenizers
- sections:
- - local: chapter4/1
- title: The Hugging Face Hub
- - local: chapter4/2
- title: Using pretrained models
- - local: chapter4/3
- title: Sharing pretrained models
- - local: chapter4/4
- title: Building a model card
- - local: chapter4/5
- title: Part 1 completed!
- - local: chapter4/6
- title: End-of-chapter quiz
- quiz: 4
-
-- title: 5. The 🤗 Datasets library
- sections:
- - local: chapter5/1
- title: Introduction
- - local: chapter5/2
- title: What if my dataset isn't on the Hub?
- - local: chapter5/3
- title: Time to slice and dice
- - local: chapter5/4
- title: Big data? 🤗 Datasets to the rescue!
- - local: chapter5/5
- title: Creating your own dataset
- - local: chapter5/6
- title: Semantic search with FAISS
- - local: chapter5/7
- title: 🤗 Datasets, check!
- - local: chapter5/8
- title: End-of-chapter quiz
- quiz: 5
-
-- title: 6. The 🤗 Tokenizers library
- sections:
- - local: chapter6/1
- title: Introduction
- - local: chapter6/2
- title: Training a new tokenizer from an old one
- - local: chapter6/3
- title: Fast tokenizers' special powers
- - local: chapter6/3b
- title: Fast tokenizers in the QA pipeline
- - local: chapter6/4
- title: Normalization and pre-tokenization
- - local: chapter6/5
- title: Byte-Pair Encoding tokenization
- - local: chapter6/6
- title: WordPiece tokenization
- - local: chapter6/7
- title: Unigram tokenization
- - local: chapter6/8
- title: Building a tokenizer, block by block
- - local: chapter6/9
- title: Tokenizers, check!
- - local: chapter6/10
- title: End-of-chapter quiz
- quiz: 6
-
-- title: 7. Classical NLP tasks
- sections:
- - local: chapter7/1
- title: Introduction
- - local: chapter7/2
- title: Token classification
- - local: chapter7/3
- title: Fine-tuning a masked language model
- - local: chapter7/4
- title: Translation
- - local: chapter7/5
- title: Summarization
- - local: chapter7/6
- title: Training a causal language model from scratch
- - local: chapter7/7
- title: Question answering
- - local: chapter7/8
- title: Mastering LLMs
- - local: chapter7/9
- title: End-of-chapter quiz
- quiz: 7
-
-- title: 8. How to ask for help
- sections:
- - local: chapter8/1
- title: Introduction
- - local: chapter8/2
- title: What to do when you get an error
- - local: chapter8/3
- title: Asking for help on the forums
- - local: chapter8/4
- title: Debugging the training pipeline
- local_fw: { pt: chapter8/4, tf: chapter8/4_tf }
- - local: chapter8/5
- title: How to write a good issue
- - local: chapter8/6
- title: Part 2 completed!
- - local: chapter8/7
- title: End-of-chapter quiz
- quiz: 8
-
-- title: 9. Building and sharing demos
- subtitle: I trained a model, but how can I show it off?
- sections:
- - local: chapter9/1
- title: Introduction to Gradio
- - local: chapter9/2
- title: Building your first demo
- - local: chapter9/3
- title: Understanding the Interface class
- - local: chapter9/4
- title: Sharing demos with others
- - local: chapter9/5
- title: Integrations with the Hugging Face Hub
- - local: chapter9/6
- title: Advanced Interface features
- - local: chapter9/7
- title: Introduction to Blocks
- - local: chapter9/8
- title: Gradio, check!
- - local: chapter9/9
- title: End-of-chapter quiz
- quiz: 9
-
-- title: 10. Curate high-quality datasets
- subtitle: How to use Argilla to create amazing datasets
- sections:
- - local: chapter10/1
- title: Introduction to Argilla
- - local: chapter10/2
- title: Set up your Argilla instance
- - local: chapter10/3
- title: Load your dataset to Argilla
- - local: chapter10/4
- title: Annotate your dataset
- - local: chapter10/5
- title: Use your annotated dataset
- - local: chapter10/6
- title: Argilla, check!
- - local: chapter10/7
- title: End-of-chapter quiz
- quiz: 10
-
-- title: 11. Fine-tune Large Language Models
- subtitle: Use Supervised Fine-tuning and Low-Rank Adaptation to fine-tune a large language model
- sections:
- - local: chapter11/1
- title: Introduction
- - local: chapter11/2
- title: Chat Templates
- - local: chapter11/3
- title: Fine-Tuning with SFTTrainer
- - local: chapter11/4
- title: LoRA (Low-Rank Adaptation)
- - local: chapter11/5
- title: Evaluation
- - local: chapter11/6
- title: Conclusion
- - local: chapter11/7
- title: Exam Time!
- quiz: 11
-
-- title: 12. Build Reasoning Models
- subtitle: Learn how to build reasoning models like DeepSeek R1
- new: true
- sections:
- - local: chapter12/1
- title: Introduction
- - local: chapter12/2
- title: Reinforcement Learning on LLMs
- - local: chapter12/3
- title: The Aha Moment in the DeepSeek R1 Paper
- - local: chapter12/3b
- title: Advanced Understanding of GRPO in DeepSeekMath
- - local: chapter12/4
- title: Implementing GRPO in TRL
- - local: chapter12/5
- title: Practical Exercise to Fine-tune a model with GRPO
- - local: chapter12/6
- title: Practical Exercise with Unsloth
- - local: chapter12/7
- title: Coming soon...
-
-- title: Course Events
- sections:
- - local: events/1
- title: Live sessions and workshops
- - local: events/2
- title: Part 2 release event
- - local: events/3
- title: Gradio Blocks party
+- title: 0. Setup
+ sections:
+ - local: chapter0/1
+ title: Introduction
+
+- title: 1. Transformer models
+ sections:
+ - local: chapter1/1
+ title: Introduction
+ - local: chapter1/2
+ title: Natural Language Processing and Large Language Models
+ - local: chapter1/3
+ title: Transformers, what can they do?
+ - local: chapter1/4
+ title: How do Transformers work?
+ - local: chapter1/5
+ title: How 🤗 Transformers solve tasks
+ - local: chapter1/6
+ title: Transformer Architectures
+ - local: chapter1/7
+ title: Quick quiz
+ - local: chapter1/8
+ title: Inference with LLMs
+ - local: chapter1/9
+ title: Bias and limitations
+ - local: chapter1/10
+ title: Summary
+ - local: chapter1/11
+ title: Certification exam
+ quiz: 1
+
+- title: 2. Using 🤗 Transformers
+ sections:
+ - local: chapter2/1
+ title: Introduction
+ - local: chapter2/2
+ title: Behind the pipeline
+ - local: chapter2/3
+ title: Models
+ - local: chapter2/4
+ title: Tokenizers
+ - local: chapter2/5
+ title: Handling multiple sequences
+ - local: chapter2/6
+ title: Putting it all together
+ - local: chapter2/7
+ title: Basic usage completed!
+ - local: chapter2/8
+ title: Optimized Inference Deployment
+ - local: chapter2/9
+ title: End-of-chapter quiz
+ quiz: 2
+
+- title: 3. Fine-tuning a pretrained model
+ sections:
+ - local: chapter3/1
+ title: Introduction
+ - local: chapter3/2
+ title: Processing the data
+ - local: chapter3/3
+ title: Fine-tuning a model with the Trainer API
+ - local: chapter3/4
+ title: A full training loop
+ - local: chapter3/5
+ title: Understanding Learning Curves
+ - local: chapter3/6
+ title: Fine-tuning, Check!
+ - local: chapter3/7
+ title: End-of-chapter quiz
+ quiz: 3
+
+- title: 4. Sharing models and tokenizers
+ sections:
+ - local: chapter4/1
+ title: The Hugging Face Hub
+ - local: chapter4/2
+ title: Using pretrained models
+ - local: chapter4/3
+ title: Sharing pretrained models
+ - local: chapter4/4
+ title: Building a model card
+ - local: chapter4/5
+ title: Part 1 completed!
+ - local: chapter4/6
+ title: End-of-chapter quiz
+ quiz: 4
+
+- title: 5. The 🤗 Datasets library
+ sections:
+ - local: chapter5/1
+ title: Introduction
+ - local: chapter5/2
+ title: What if my dataset isn't on the Hub?
+ - local: chapter5/3
+ title: Time to slice and dice
+ - local: chapter5/4
+ title: Big data? 🤗 Datasets to the rescue!
+ - local: chapter5/5
+ title: Creating your own dataset
+ - local: chapter5/6
+ title: Semantic search with FAISS
+ - local: chapter5/7
+ title: 🤗 Datasets, check!
+ - local: chapter5/8
+ title: End-of-chapter quiz
+ quiz: 5
+
+- title: 6. The 🤗 Tokenizers library
+ sections:
+ - local: chapter6/1
+ title: Introduction
+ - local: chapter6/2
+ title: Training a new tokenizer from an old one
+ - local: chapter6/3
+ title: Fast tokenizers' special powers
+ - local: chapter6/3b
+ title: Fast tokenizers in the QA pipeline
+ - local: chapter6/4
+ title: Normalization and pre-tokenization
+ - local: chapter6/5
+ title: Byte-Pair Encoding tokenization
+ - local: chapter6/6
+ title: WordPiece tokenization
+ - local: chapter6/7
+ title: Unigram tokenization
+ - local: chapter6/8
+ title: Building a tokenizer, block by block
+ - local: chapter6/9
+ title: Tokenizers, check!
+ - local: chapter6/10
+ title: End-of-chapter quiz
+ quiz: 6
+
+- title: 7. Classical NLP tasks
+ sections:
+ - local: chapter7/1
+ title: Introduction
+ - local: chapter7/2
+ title: Token classification
+ - local: chapter7/3
+ title: Fine-tuning a masked language model
+ - local: chapter7/4
+ title: Translation
+ - local: chapter7/5
+ title: Summarization
+ - local: chapter7/6
+ title: Training a causal language model from scratch
+ - local: chapter7/7
+ title: Question answering
+ - local: chapter7/8
+ title: Mastering LLMs
+ - local: chapter7/9
+ title: End-of-chapter quiz
+ quiz: 7
+
+- title: 8. How to ask for help
+ sections:
+ - local: chapter8/1
+ title: Introduction
+ - local: chapter8/2
+ title: What to do when you get an error
+ - local: chapter8/3
+ title: Asking for help on the forums
+ - local: chapter8/4
+ title: Debugging the training pipeline
+ local_fw: { pt: chapter8/4, tf: chapter8/4_tf }
+ - local: chapter8/5
+ title: How to write a good issue
+ - local: chapter8/6
+ title: Part 2 completed!
+ - local: chapter8/7
+ title: End-of-chapter quiz
+ quiz: 8
+
+- title: 9. Building and sharing demos
+ subtitle: I trained a model, but how can I show it off?
+ sections:
+ - local: chapter9/1
+ title: Introduction to Gradio
+ - local: chapter9/2
+ title: Building your first demo
+ - local: chapter9/3
+ title: Understanding the Interface class
+ - local: chapter9/4
+ title: Sharing demos with others
+ - local: chapter9/5
+ title: Integrations with the Hugging Face Hub
+ - local: chapter9/6
+ title: Advanced Interface features
+ - local: chapter9/7
+ title: Introduction to Blocks
+ - local: chapter9/8
+ title: Gradio, check!
+ - local: chapter9/9
+ title: End-of-chapter quiz
+ quiz: 9
+
+- title: 10. Curate high-quality datasets
+ subtitle: How to use Argilla to create amazing datasets
+ sections:
+ - local: chapter10/1
+ title: Introduction to Argilla
+ - local: chapter10/2
+ title: Set up your Argilla instance
+ - local: chapter10/3
+ title: Load your dataset to Argilla
+ - local: chapter10/4
+ title: Annotate your dataset
+ - local: chapter10/5
+ title: Use your annotated dataset
+ - local: chapter10/6
+ title: Argilla, check!
+ - local: chapter10/7
+ title: End-of-chapter quiz
+ quiz: 10
+
+- title: 11. Fine-tune Large Language Models
+ subtitle: Use Supervised Fine-tuning and Low-Rank Adaptation to fine-tune a large language model
+ sections:
+ - local: chapter11/1
+ title: Introduction
+ - local: chapter11/2
+ title: Chat Templates
+ - local: chapter11/3
+ title: Fine-Tuning with SFTTrainer
+ - local: chapter11/4
+ title: LoRA (Low-Rank Adaptation)
+ - local: chapter11/5
+ title: Evaluation
+ - local: chapter11/6
+ title: Conclusion
+ - local: chapter11/7
+ title: Exam Time!
+ quiz: 11
+
+- title: 12. Build Reasoning Models
+ subtitle: Learn how to build reasoning models like DeepSeek R1
+ new: true
+ sections:
+ - local: chapter12/1
+ title: Introduction
+ - local: chapter12/2
+ title: Reinforcement Learning on LLMs
+ - local: chapter12/3
+ title: The Aha Moment in the DeepSeek R1 Paper
+ - local: chapter12/3b
+ title: Advanced Understanding of GRPO in DeepSeekMath
+ - local: chapter12/4
+ title: Implementing GRPO in TRL
+ - local: chapter12/5
+ title: Practical Exercise to Fine-tune a model with GRPO
+ - local: chapter12/6
+ title: Practical Exercise with Unsloth
+ - local: chapter12/7
+ title: Coming soon...
+
+- title: Course Events
+ sections:
+ - local: events/1
+ title: Live sessions and workshops
+ - local: events/2
+ title: Part 2 release event
+ - local: events/3
+ title: Gradio Blocks party
diff --git a/chapters/en/chapter0/1.mdx b/chapters/en/chapter0/1.mdx
index 40e21bf91..726b0ece8 100644
--- a/chapters/en/chapter0/1.mdx
+++ b/chapters/en/chapter0/1.mdx
@@ -1,110 +1,110 @@
-# Introduction[[introduction]]
-
-Welcome to the Hugging Face course! This introduction will guide you through setting up a working environment. If you're just starting the course, we recommend you first take a look at [Chapter 1](/course/chapter1), then come back and set up your environment so you can try the code yourself.
-
-All the libraries that we'll be using in this course are available as Python packages, so here we'll show you how to set up a Python environment and install the specific libraries you'll need.
-
-We'll cover two ways of setting up your working environment, using a Colab notebook or a Python virtual environment. Feel free to choose the one that resonates with you the most. For beginners, we strongly recommend that you get started by using a Colab notebook.
-
-Note that we will not be covering the Windows system. If you're running on Windows, we recommend following along using a Colab notebook. If you're using a Linux distribution or macOS, you can use either approach described here.
-
-Most of the course relies on you having a Hugging Face account. We recommend creating one now: [create an account](https://huggingface.co/join).
-
-## Using a Google Colab notebook[[using-a-google-colab-notebook]]
-
-Using a Colab notebook is the simplest possible setup; boot up a notebook in your browser and get straight to coding!
-
-If you're not familiar with Colab, we recommend you start by following the [introduction](https://colab.research.google.com/notebooks/intro.ipynb). Colab allows you to use some accelerating hardware, like GPUs or TPUs, and it is free for smaller workloads.
-
-Once you're comfortable moving around in Colab, create a new notebook and get started with the setup:
-
-
-

-
-
-The next step is to install the libraries that we'll be using in this course. We'll use `pip` for the installation, which is the package manager for Python. In notebooks, you can run system commands by preceding them with the `!` character, so you can install the 🤗 Transformers library as follows:
-
-```
-!pip install transformers
-```
-
-You can make sure the package was correctly installed by importing it within your Python runtime:
-
-```
-import transformers
-```
-
-
-

-
-
-This installs a very light version of 🤗 Transformers. In particular, no specific machine learning frameworks (like PyTorch or TensorFlow) are installed. Since we'll be using a lot of different features of the library, we recommend installing the development version, which comes with all the required dependencies for pretty much any imaginable use case:
-
-```
-!pip install transformers[sentencepiece]
-```
-
-This will take a bit of time, but then you'll be ready to go for the rest of the course!
-
-## Using a Python virtual environment[[using-a-python-virtual-environment]]
-
-If you prefer to use a Python virtual environment, the first step is to install Python on your system. We recommend following [this guide](https://realpython.com/installing-python/) to get started.
-
-Once you have Python installed, you should be able to run Python commands in your terminal. You can start by running the following command to ensure that it is correctly installed before proceeding to the next steps: `python --version`. This should print out the Python version now available on your system.
-
-When running a Python command in your terminal, such as `python --version`, you should think of the program running your command as the "main" Python on your system. We recommend keeping this main installation free of any packages, and using it to create separate environments for each application you work on — this way, each application can have its own dependencies and packages, and you won't need to worry about potential compatibility issues with other applications.
-
-In Python this is done with [*virtual environments*](https://docs.python.org/3/tutorial/venv.html), which are self-contained directory trees that each contain a Python installation with a particular Python version alongside all the packages the application needs. Creating such a virtual environment can be done with a number of different tools, but we'll use the official Python package for that purpose, which is called [`venv`](https://docs.python.org/3/library/venv.html#module-venv).
-
-First, create the directory you'd like your application to live in — for example, you might want to make a new directory called *transformers-course* at the root of your home directory:
-
-```
-mkdir ~/transformers-course
-cd ~/transformers-course
-```
-
-From inside this directory, create a virtual environment using the Python `venv` module:
-
-```
-python -m venv .env
-```
-
-You should now have a directory called *.env* in your otherwise empty folder:
-
-```
-ls -a
-```
-
-```out
-. .. .env
-```
-
-You can jump in and out of your virtual environment with the `activate` and `deactivate` scripts:
-
-```
-# Activate the virtual environment
-source .env/bin/activate
-
-# Deactivate the virtual environment
-deactivate
-```
-
-You can make sure that the environment is activated by running the `which python` command: if it points to the virtual environment, then you have successfully activated it!
-
-```
-which python
-```
-
-```out
-/home//transformers-course/.env/bin/python
-```
-
-### Installing dependencies[[installing-dependencies]]
-
-As in the previous section on using Google Colab instances, you'll now need to install the packages required to continue. Again, you can install the development version of 🤗 Transformers using the `pip` package manager:
-
-```
-pip install "transformers[sentencepiece]"
-```
-
-You're now all set up and ready to go!
+# Introduction[[introduction]]
+
+Welcome to the Hugging Face course! This introduction will guide you through setting up a working environment. If you're just starting the course, we recommend you first take a look at [Chapter 1](/course/chapter1), then come back and set up your environment so you can try the code yourself.
+
+All the libraries that we'll be using in this course are available as Python packages, so here we'll show you how to set up a Python environment and install the specific libraries you'll need.
+
+We'll cover two ways of setting up your working environment, using a Colab notebook or a Python virtual environment. Feel free to choose the one that resonates with you the most. For beginners, we strongly recommend that you get started by using a Colab notebook.
+
+Note that we will not be covering the Windows system. If you're running on Windows, we recommend following along using a Colab notebook. If you're using a Linux distribution or macOS, you can use either approach described here.
+
+Most of the course relies on you having a Hugging Face account. We recommend creating one now: [create an account](https://huggingface.co/join).
+
+## Using a Google Colab notebook[[using-a-google-colab-notebook]]
+
+Using a Colab notebook is the simplest possible setup; boot up a notebook in your browser and get straight to coding!
+
+If you're not familiar with Colab, we recommend you start by following the [introduction](https://colab.research.google.com/notebooks/intro.ipynb). Colab allows you to use some accelerating hardware, like GPUs or TPUs, and it is free for smaller workloads.
+
+Once you're comfortable moving around in Colab, create a new notebook and get started with the setup:
+
+
+

+
+
+The next step is to install the libraries that we'll be using in this course. We'll use `pip` for the installation, which is the package manager for Python. In notebooks, you can run system commands by preceding them with the `!` character, so you can install the 🤗 Transformers library as follows:
+
+```
+!pip install transformers
+```
+
+You can make sure the package was correctly installed by importing it within your Python runtime:
+
+```
+import transformers
+```
+
+
+

+
+
+This installs a very light version of 🤗 Transformers. In particular, no specific machine learning frameworks (like PyTorch or TensorFlow) are installed. Since we'll be using a lot of different features of the library, we recommend installing the development version, which comes with all the required dependencies for pretty much any imaginable use case:
+
+```
+!pip install transformers[sentencepiece]
+```
+
+This will take a bit of time, but then you'll be ready to go for the rest of the course!
+
+## Using a Python virtual environment[[using-a-python-virtual-environment]]
+
+If you prefer to use a Python virtual environment, the first step is to install Python on your system. We recommend following [this guide](https://realpython.com/installing-python/) to get started.
+
+Once you have Python installed, you should be able to run Python commands in your terminal. You can start by running the following command to ensure that it is correctly installed before proceeding to the next steps: `python --version`. This should print out the Python version now available on your system.
+
+When running a Python command in your terminal, such as `python --version`, you should think of the program running your command as the "main" Python on your system. We recommend keeping this main installation free of any packages, and using it to create separate environments for each application you work on — this way, each application can have its own dependencies and packages, and you won't need to worry about potential compatibility issues with other applications.
+
+In Python this is done with [*virtual environments*](https://docs.python.org/3/tutorial/venv.html), which are self-contained directory trees that each contain a Python installation with a particular Python version alongside all the packages the application needs. Creating such a virtual environment can be done with a number of different tools, but we'll use the official Python package for that purpose, which is called [`venv`](https://docs.python.org/3/library/venv.html#module-venv).
+
+First, create the directory you'd like your application to live in — for example, you might want to make a new directory called *transformers-course* at the root of your home directory:
+
+```
+mkdir ~/transformers-course
+cd ~/transformers-course
+```
+
+From inside this directory, create a virtual environment using the Python `venv` module:
+
+```
+python -m venv .env
+```
+
+You should now have a directory called *.env* in your otherwise empty folder:
+
+```
+ls -a
+```
+
+```out
+. .. .env
+```
+
+You can jump in and out of your virtual environment with the `activate` and `deactivate` scripts:
+
+```
+# Activate the virtual environment
+source .env/bin/activate
+
+# Deactivate the virtual environment
+deactivate
+```
+
+You can make sure that the environment is activated by running the `which python` command: if it points to the virtual environment, then you have successfully activated it!
+
+```
+which python
+```
+
+```out
+/home//transformers-course/.env/bin/python
+```
+
+### Installing dependencies[[installing-dependencies]]
+
+As in the previous section on using Google Colab instances, you'll now need to install the packages required to continue. Again, you can install the development version of 🤗 Transformers using the `pip` package manager:
+
+```
+pip install "transformers[sentencepiece]"
+```
+
+You're now all set up and ready to go!
diff --git a/chapters/en/chapter1/1.mdx b/chapters/en/chapter1/1.mdx
index 9294f9af1..107bd03f8 100644
--- a/chapters/en/chapter1/1.mdx
+++ b/chapters/en/chapter1/1.mdx
@@ -1,159 +1,159 @@
-# Introduction[[introduction]]
-
-
-
-## Welcome to the 🤗 Course![[welcome-to-the-course]]
-
-
-
-This course will teach you about large language models (LLMs) and natural language processing (NLP) using libraries from the [Hugging Face](https://huggingface.co/) ecosystem — [🤗 Transformers](https://github.com/huggingface/transformers), [🤗 Datasets](https://github.com/huggingface/datasets), [🤗 Tokenizers](https://github.com/huggingface/tokenizers), and [🤗 Accelerate](https://github.com/huggingface/accelerate) — as well as the [Hugging Face Hub](https://huggingface.co/models).
-
-We'll also cover libraries outside the Hugging Face ecosystem. These are amazing contributions to the AI community and incredibly useful tools.
-
-It's completely free and without ads.
-
-## Understanding NLP and LLMs[[understanding-nlp-and-llms]]
-
-While this course was originally focused on NLP (Natural Language Processing), it has evolved to emphasize Large Language Models (LLMs), which represent the latest advancement in the field.
-
-**What's the difference?**
-- **NLP (Natural Language Processing)** is the broader field focused on enabling computers to understand, interpret, and generate human language. NLP encompasses many techniques and tasks such as sentiment analysis, named entity recognition, and machine translation.
-- **LLMs (Large Language Models)** are a powerful subset of NLP models characterized by their massive size, extensive training data, and ability to perform a wide range of language tasks with minimal task-specific training. Models like the Llama, GPT, or Claude series are examples of LLMs that have revolutionized what's possible in NLP.
-
-Throughout this course, you'll learn about both traditional NLP concepts and cutting-edge LLM techniques, as understanding the foundations of NLP is crucial for working effectively with LLMs.
-
-## What to expect?[[what-to-expect]]
-
-Here is a brief overview of the course:
-
-
-

-

-
-
-- Chapters 1 to 4 provide an introduction to the main concepts of the 🤗 Transformers library. By the end of this part of the course, you will be familiar with how Transformer models work and will know how to use a model from the [Hugging Face Hub](https://huggingface.co/models), fine-tune it on a dataset, and share your results on the Hub!
-- Chapters 5 to 8 teach the basics of 🤗 Datasets and 🤗 Tokenizers before diving into classic NLP tasks and LLM techniques. By the end of this part, you will be able to tackle the most common language processing challenges by yourself.
-- Chapter 9 goes beyond NLP to cover how to build and share demos of your models on the 🤗 Hub. By the end of this part, you will be ready to showcase your 🤗 Transformers application to the world!
-- Chapters 10 to 12 dive into advanced LLM topics like fine-tuning, curating high-quality datasets, and building reasoning models.
-
-This course:
-
-* Requires a good knowledge of Python
-* Is better taken after an introductory deep learning course, such as [fast.ai's](https://www.fast.ai/) [Practical Deep Learning for Coders](https://course.fast.ai/) or one of the programs developed by [DeepLearning.AI](https://www.deeplearning.ai/)
-* Does not expect prior [PyTorch](https://pytorch.org/) or [TensorFlow](https://www.tensorflow.org/) knowledge, though some familiarity with either of those will help
-
-After you've completed this course, we recommend checking out DeepLearning.AI's [Natural Language Processing Specialization](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh), which covers a wide range of traditional NLP models like naive Bayes and LSTMs that are well worth knowing about!
-
-## Who are we?[[who-are-we]]
-
-About the authors:
-
-[**Abubakar Abid**](https://huggingface.co/abidlabs) completed his PhD at Stanford in applied machine learning. During his PhD, he founded [Gradio](https://github.com/gradio-app/gradio), an open-source Python library that has been used to build over 600,000 machine learning demos. Gradio was acquired by Hugging Face, which is where Abubakar now serves as a machine learning team lead.
-
-[**Ben Burtenshaw**](https://huggingface.co/burtenshaw) is a Machine Learning Engineer at Hugging Face. He completed his PhD in Natural Language Processing at the University of Antwerp, where he applied Transformer models to generate children stories for the purpose of improving literacy skills. Since then, he has focused on educational materials and tools for the wider community.
-
-[**Matthew Carrigan**](https://huggingface.co/Rocketknight1) is a Machine Learning Engineer at Hugging Face. He lives in Dublin, Ireland and previously worked as an ML engineer at Parse.ly and before that as a post-doctoral researcher at Trinity College Dublin. He does not believe we're going to get to AGI by scaling existing architectures, but has high hopes for robot immortality regardless.
-
-[**Lysandre Debut**](https://huggingface.co/lysandre) is a Machine Learning Engineer at Hugging Face and has been working on the 🤗 Transformers library since the very early development stages. His aim is to make NLP accessible for everyone by developing tools with a very simple API.
-
-[**Sylvain Gugger**](https://huggingface.co/sgugger) is a Research Engineer at Hugging Face and one of the core maintainers of the 🤗 Transformers library. Previously he was a Research Scientist at fast.ai, and he co-wrote _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ with Jeremy Howard. The main focus of his research is on making deep learning more accessible, by designing and improving techniques that allow models to train fast on limited resources.
-
-[**Dawood Khan**](https://huggingface.co/dawoodkhan82) is a Machine Learning Engineer at Hugging Face. He's from NYC and graduated from New York University studying Computer Science. After working as an iOS Engineer for a few years, Dawood quit to start Gradio with his fellow co-founders. Gradio was eventually acquired by Hugging Face.
-
-[**Merve Noyan**](https://huggingface.co/merve) is a developer advocate at Hugging Face, working on developing tools and building content around them to democratize machine learning for everyone.
-
-[**Lucile Saulnier**](https://huggingface.co/SaulLu) is a machine learning engineer at Hugging Face, developing and supporting the use of open source tools. She is also actively involved in many research projects in the field of Natural Language Processing such as collaborative training and BigScience.
-
-[**Lewis Tunstall**](https://huggingface.co/lewtun) is a machine learning engineer at Hugging Face, focused on developing open-source tools and making them accessible to the wider community. He is also a co-author of the O'Reilly book [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).
-
-[**Leandro von Werra**](https://huggingface.co/lvwerra) is a machine learning engineer in the open-source team at Hugging Face and also a co-author of the O'Reilly book [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). He has several years of industry experience bringing NLP projects to production by working across the whole machine learning stack..
-
-## FAQ[[faq]]
-
-Here are some answers to frequently asked questions:
-
-- **Does taking this course lead to a certification?**
-Currently we do not have any certification for this course. However, we are working on a certification program for the Hugging Face ecosystem -- stay tuned!
-
-- **How much time should I spend on this course?**
-Each chapter in this course is designed to be completed in 1 week, with approximately 6-8 hours of work per week. However, you can take as much time as you need to complete the course.
-
-- **Where can I ask a question if I have one?**
-If you have a question about any section of the course, just click on the "*Ask a question*" banner at the top of the page to be automatically redirected to the right section of the [Hugging Face forums](https://discuss.huggingface.co/):
-
-
-
-Note that a list of [project ideas](https://discuss.huggingface.co/c/course/course-event/25) is also available on the forums if you wish to practice more once you have completed the course.
-
-- **Where can I get the code for the course?**
-For each section, click on the banner at the top of the page to run the code in either Google Colab or Amazon SageMaker Studio Lab:
-
-
-
-The Jupyter notebooks containing all the code from the course are hosted on the [`huggingface/notebooks`](https://github.com/huggingface/notebooks) repo. If you wish to generate them locally, check out the instructions in the [`course`](https://github.com/huggingface/course#-jupyter-notebooks) repo on GitHub.
-
-
-- **How can I contribute to the course?**
-There are many ways to contribute to the course! If you find a typo or a bug, please open an issue on the [`course`](https://github.com/huggingface/course) repo. If you would like to help translate the course into your native language, check out the instructions [here](https://github.com/huggingface/course#translating-the-course-into-your-language).
-
-- ** What were the choices made for each translation?**
-Each translation has a glossary and `TRANSLATING.txt` file that details the choices that were made for machine learning jargon etc. You can find an example for German [here](https://github.com/huggingface/course/blob/main/chapters/de/TRANSLATING.txt).
-
-
-- **Can I reuse this course?**
-Of course! The course is released under the permissive [Apache 2 license](https://www.apache.org/licenses/LICENSE-2.0.html). This means that you must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. If you would like to cite the course, please use the following BibTeX:
-
-```
-@misc{huggingfacecourse,
- author = {Hugging Face},
- title = {The Hugging Face Course, 2022},
- howpublished = "\url{https://huggingface.co/course}",
- year = {2022},
- note = "[Online; accessed ]"
-}
-```
-
-## Languages and translations[[languages-and-translations]]
-
-Thanks to our wonderful community, the course is available in many languages beyond English 🔥! Check out the table below to see which languages are available and who contributed to the translations:
-
-| Language | Authors |
-|:------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| [French](https://huggingface.co/course/fr/chapter1/1) | [@lbourdois](https://github.com/lbourdois), [@ChainYo](https://github.com/ChainYo), [@melaniedrevet](https://github.com/melaniedrevet), [@abdouaziz](https://github.com/abdouaziz) |
-| [Vietnamese](https://huggingface.co/course/vi/chapter1/1) | [@honghanhh](https://github.com/honghanhh) |
-| [Chinese (simplified)](https://huggingface.co/course/zh-CN/chapter1/1) | [@zhlhyx](https://github.com/zhlhyx), [petrichor1122](https://github.com/petrichor1122), [@yaoqih](https://github.com/yaoqih) |
-| [Bengali](https://huggingface.co/course/bn/chapter1/1) (WIP) | [@avishek-018](https://github.com/avishek-018), [@eNipu](https://github.com/eNipu) |
-| [German](https://huggingface.co/course/de/chapter1/1) (WIP) | [@JesperDramsch](https://github.com/JesperDramsch), [@MarcusFra](https://github.com/MarcusFra), [@fabridamicelli](https://github.com/fabridamicelli) |
-| [Spanish](https://huggingface.co/course/es/chapter1/1) (WIP) | [@camartinezbu](https://github.com/camartinezbu), [@munozariasjm](https://github.com/munozariasjm), [@fordaz](https://github.com/fordaz) |
-| [Persian](https://huggingface.co/course/fa/chapter1/1) (WIP) | [@jowharshamshiri](https://github.com/jowharshamshiri), [@schoobani](https://github.com/schoobani) |
-| [Gujarati](https://huggingface.co/course/gu/chapter1/1) (WIP) | [@pandyaved98](https://github.com/pandyaved98) |
-| [Hebrew](https://huggingface.co/course/he/chapter1/1) (WIP) | [@omer-dor](https://github.com/omer-dor) |
-| [Hindi](https://huggingface.co/course/hi/chapter1/1) (WIP) | [@pandyaved98](https://github.com/pandyaved98) |
-| [Bahasa Indonesia](https://huggingface.co/course/id/chapter1/1) (WIP) | [@gstdl](https://github.com/gstdl) |
-| [Italian](https://huggingface.co/course/it/chapter1/1) (WIP) | [@CaterinaBi](https://github.com/CaterinaBi), [@ClonedOne](https://github.com/ClonedOne), [@Nolanogenn](https://github.com/Nolanogenn), [@EdAbati](https://github.com/EdAbati), [@gdacciaro](https://github.com/gdacciaro) |
-| [Japanese](https://huggingface.co/course/ja/chapter1/1) (WIP) | [@hiromu166](https://github.com/@hiromu166), [@younesbelkada](https://github.com/@younesbelkada), [@HiromuHota](https://github.com/@HiromuHota) |
-| [Korean](https://huggingface.co/course/ko/chapter1/1) (WIP) | [@Doohae](https://github.com/Doohae), [@wonhyeongseo](https://github.com/wonhyeongseo), [@dlfrnaos19](https://github.com/dlfrnaos19) |
-| [Portuguese](https://huggingface.co/course/pt/chapter1/1) (WIP) | [@johnnv1](https://github.com/johnnv1), [@victorescosta](https://github.com/victorescosta), [@LincolnVS](https://github.com/LincolnVS) |
-| [Russian](https://huggingface.co/course/ru/chapter1/1) (WIP) | [@pdumin](https://github.com/pdumin), [@svv73](https://github.com/svv73) |
-| [Thai](https://huggingface.co/course/th/chapter1/1) (WIP) | [@peeraponw](https://github.com/peeraponw), [@a-krirk](https://github.com/a-krirk), [@jomariya23156](https://github.com/jomariya23156), [@ckingkan](https://github.com/ckingkan) |
-| [Turkish](https://huggingface.co/course/tr/chapter1/1) (WIP) | [@tanersekmen](https://github.com/tanersekmen), [@mertbozkir](https://github.com/mertbozkir), [@ftarlaci](https://github.com/ftarlaci), [@akkasayaz](https://github.com/akkasayaz) |
-| [Chinese (traditional)](https://huggingface.co/course/zh-TW/chapter1/1) (WIP) | [@davidpeng86](https://github.com/davidpeng86) |
-
-For some languages, the [course YouTube videos](https://youtube.com/playlist?list=PLo2EIpI_JMQvWfQndUesu0nPBAtZ9gP1o) have subtitles in the language. You can enable them by first clicking the _CC_ button in the bottom right corner of the video. Then, under the settings icon ⚙️, you can select the language you want by selecting the _Subtitles/CC_ option.
-
-
-
-> [!TIP]
-> Don't see your language in the above table or you'd like to contribute to an existing translation? You can help us translate the course by following the instructions here.
-
-## Let's go 🚀
-
-Are you ready to roll? In this chapter, you will learn:
-
-* How to use the `pipeline()` function to solve NLP tasks such as text generation and classification
-* About the Transformer architecture
-* How to distinguish between encoder, decoder, and encoder-decoder architectures and use cases
-
+# Introduction[[introduction]]
+
+
+
+## Welcome to the 🤗 Course![[welcome-to-the-course]]
+
+
+
+This course will teach you about large language models (LLMs) and natural language processing (NLP) using libraries from the [Hugging Face](https://huggingface.co/) ecosystem — [🤗 Transformers](https://github.com/huggingface/transformers), [🤗 Datasets](https://github.com/huggingface/datasets), [🤗 Tokenizers](https://github.com/huggingface/tokenizers), and [🤗 Accelerate](https://github.com/huggingface/accelerate) — as well as the [Hugging Face Hub](https://huggingface.co/models).
+
+We'll also cover libraries outside the Hugging Face ecosystem. These are amazing contributions to the AI community and incredibly useful tools.
+
+It's completely free and without ads.
+
+## Understanding NLP and LLMs[[understanding-nlp-and-llms]]
+
+While this course was originally focused on NLP (Natural Language Processing), it has evolved to emphasize Large Language Models (LLMs), which represent the latest advancement in the field.
+
+**What's the difference?**
+- **NLP (Natural Language Processing)** is the broader field focused on enabling computers to understand, interpret, and generate human language. NLP encompasses many techniques and tasks such as sentiment analysis, named entity recognition, and machine translation.
+- **LLMs (Large Language Models)** are a powerful subset of NLP models characterized by their massive size, extensive training data, and ability to perform a wide range of language tasks with minimal task-specific training. Models like the Llama, GPT, or Claude series are examples of LLMs that have revolutionized what's possible in NLP.
+
+Throughout this course, you'll learn about both traditional NLP concepts and cutting-edge LLM techniques, as understanding the foundations of NLP is crucial for working effectively with LLMs.
+
+## What to expect?[[what-to-expect]]
+
+Here is a brief overview of the course:
+
+
+

+

+
+
+- Chapters 1 to 4 provide an introduction to the main concepts of the 🤗 Transformers library. By the end of this part of the course, you will be familiar with how Transformer models work and will know how to use a model from the [Hugging Face Hub](https://huggingface.co/models), fine-tune it on a dataset, and share your results on the Hub!
+- Chapters 5 to 8 teach the basics of 🤗 Datasets and 🤗 Tokenizers before diving into classic NLP tasks and LLM techniques. By the end of this part, you will be able to tackle the most common language processing challenges by yourself.
+- Chapter 9 goes beyond NLP to cover how to build and share demos of your models on the 🤗 Hub. By the end of this part, you will be ready to showcase your 🤗 Transformers application to the world!
+- Chapters 10 to 12 dive into advanced LLM topics like fine-tuning, curating high-quality datasets, and building reasoning models.
+
+This course:
+
+* Requires a good knowledge of Python
+* Is better taken after an introductory deep learning course, such as [fast.ai's](https://www.fast.ai/) [Practical Deep Learning for Coders](https://course.fast.ai/) or one of the programs developed by [DeepLearning.AI](https://www.deeplearning.ai/)
+* Does not expect prior [PyTorch](https://pytorch.org/) or [TensorFlow](https://www.tensorflow.org/) knowledge, though some familiarity with either of those will help
+
+After you've completed this course, we recommend checking out DeepLearning.AI's [Natural Language Processing Specialization](https://www.coursera.org/specializations/natural-language-processing?utm_source=deeplearning-ai&utm_medium=institutions&utm_campaign=20211011-nlp-2-hugging_face-page-nlp-refresh), which covers a wide range of traditional NLP models like naive Bayes and LSTMs that are well worth knowing about!
+
+## Who are we?[[who-are-we]]
+
+About the authors:
+
+[**Abubakar Abid**](https://huggingface.co/abidlabs) completed his PhD at Stanford in applied machine learning. During his PhD, he founded [Gradio](https://github.com/gradio-app/gradio), an open-source Python library that has been used to build over 600,000 machine learning demos. Gradio was acquired by Hugging Face, which is where Abubakar now serves as a machine learning team lead.
+
+[**Ben Burtenshaw**](https://huggingface.co/burtenshaw) is a Machine Learning Engineer at Hugging Face. He completed his PhD in Natural Language Processing at the University of Antwerp, where he applied Transformer models to generate children stories for the purpose of improving literacy skills. Since then, he has focused on educational materials and tools for the wider community.
+
+[**Matthew Carrigan**](https://huggingface.co/Rocketknight1) is a Machine Learning Engineer at Hugging Face. He lives in Dublin, Ireland and previously worked as an ML engineer at Parse.ly and before that as a post-doctoral researcher at Trinity College Dublin. He does not believe we're going to get to AGI by scaling existing architectures, but has high hopes for robot immortality regardless.
+
+[**Lysandre Debut**](https://huggingface.co/lysandre) is a Machine Learning Engineer at Hugging Face and has been working on the 🤗 Transformers library since the very early development stages. His aim is to make NLP accessible for everyone by developing tools with a very simple API.
+
+[**Sylvain Gugger**](https://huggingface.co/sgugger) is a Research Engineer at Hugging Face and one of the core maintainers of the 🤗 Transformers library. Previously he was a Research Scientist at fast.ai, and he co-wrote _[Deep Learning for Coders with fastai and PyTorch](https://learning.oreilly.com/library/view/deep-learning-for/9781492045519/)_ with Jeremy Howard. The main focus of his research is on making deep learning more accessible, by designing and improving techniques that allow models to train fast on limited resources.
+
+[**Dawood Khan**](https://huggingface.co/dawoodkhan82) is a Machine Learning Engineer at Hugging Face. He's from NYC and graduated from New York University studying Computer Science. After working as an iOS Engineer for a few years, Dawood quit to start Gradio with his fellow co-founders. Gradio was eventually acquired by Hugging Face.
+
+[**Merve Noyan**](https://huggingface.co/merve) is a developer advocate at Hugging Face, working on developing tools and building content around them to democratize machine learning for everyone.
+
+[**Lucile Saulnier**](https://huggingface.co/SaulLu) is a machine learning engineer at Hugging Face, developing and supporting the use of open source tools. She is also actively involved in many research projects in the field of Natural Language Processing such as collaborative training and BigScience.
+
+[**Lewis Tunstall**](https://huggingface.co/lewtun) is a machine learning engineer at Hugging Face, focused on developing open-source tools and making them accessible to the wider community. He is also a co-author of the O'Reilly book [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/).
+
+[**Leandro von Werra**](https://huggingface.co/lvwerra) is a machine learning engineer in the open-source team at Hugging Face and also a co-author of the O'Reilly book [Natural Language Processing with Transformers](https://www.oreilly.com/library/view/natural-language-processing/9781098136789/). He has several years of industry experience bringing NLP projects to production by working across the whole machine learning stack..
+
+## FAQ[[faq]]
+
+Here are some answers to frequently asked questions:
+
+- **Does taking this course lead to a certification?**
+Currently we do not have any certification for this course. However, we are working on a certification program for the Hugging Face ecosystem -- stay tuned!
+
+- **How much time should I spend on this course?**
+Each chapter in this course is designed to be completed in 1 week, with approximately 6-8 hours of work per week. However, you can take as much time as you need to complete the course.
+
+- **Where can I ask a question if I have one?**
+If you have a question about any section of the course, just click on the "*Ask a question*" banner at the top of the page to be automatically redirected to the right section of the [Hugging Face forums](https://discuss.huggingface.co/):
+
+
+
+Note that a list of [project ideas](https://discuss.huggingface.co/c/course/course-event/25) is also available on the forums if you wish to practice more once you have completed the course.
+
+- **Where can I get the code for the course?**
+For each section, click on the banner at the top of the page to run the code in either Google Colab or Amazon SageMaker Studio Lab:
+
+
+
+The Jupyter notebooks containing all the code from the course are hosted on the [`huggingface/notebooks`](https://github.com/huggingface/notebooks) repo. If you wish to generate them locally, check out the instructions in the [`course`](https://github.com/huggingface/course#-jupyter-notebooks) repo on GitHub.
+
+
+- **How can I contribute to the course?**
+There are many ways to contribute to the course! If you find a typo or a bug, please open an issue on the [`course`](https://github.com/huggingface/course) repo. If you would like to help translate the course into your native language, check out the instructions [here](https://github.com/huggingface/course#translating-the-course-into-your-language).
+
+- ** What were the choices made for each translation?**
+Each translation has a glossary and `TRANSLATING.txt` file that details the choices that were made for machine learning jargon etc. You can find an example for German [here](https://github.com/huggingface/course/blob/main/chapters/de/TRANSLATING.txt).
+
+
+- **Can I reuse this course?**
+Of course! The course is released under the permissive [Apache 2 license](https://www.apache.org/licenses/LICENSE-2.0.html). This means that you must give appropriate credit, provide a link to the license, and indicate if changes were made. You may do so in any reasonable manner, but not in any way that suggests the licensor endorses you or your use. If you would like to cite the course, please use the following BibTeX:
+
+```
+@misc{huggingfacecourse,
+ author = {Hugging Face},
+ title = {The Hugging Face Course, 2022},
+ howpublished = "\url{https://huggingface.co/course}",
+ year = {2022},
+ note = "[Online; accessed ]"
+}
+```
+
+## Languages and translations[[languages-and-translations]]
+
+Thanks to our wonderful community, the course is available in many languages beyond English 🔥! Check out the table below to see which languages are available and who contributed to the translations:
+
+| Language | Authors |
+|:------------------------------------------------------------------------------|:---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| [French](https://huggingface.co/course/fr/chapter1/1) | [@lbourdois](https://github.com/lbourdois), [@ChainYo](https://github.com/ChainYo), [@melaniedrevet](https://github.com/melaniedrevet), [@abdouaziz](https://github.com/abdouaziz) |
+| [Vietnamese](https://huggingface.co/course/vi/chapter1/1) | [@honghanhh](https://github.com/honghanhh) |
+| [Chinese (simplified)](https://huggingface.co/course/zh-CN/chapter1/1) | [@zhlhyx](https://github.com/zhlhyx), [petrichor1122](https://github.com/petrichor1122), [@yaoqih](https://github.com/yaoqih) |
+| [Bengali](https://huggingface.co/course/bn/chapter1/1) (WIP) | [@avishek-018](https://github.com/avishek-018), [@eNipu](https://github.com/eNipu) |
+| [German](https://huggingface.co/course/de/chapter1/1) (WIP) | [@JesperDramsch](https://github.com/JesperDramsch), [@MarcusFra](https://github.com/MarcusFra), [@fabridamicelli](https://github.com/fabridamicelli) |
+| [Spanish](https://huggingface.co/course/es/chapter1/1) (WIP) | [@camartinezbu](https://github.com/camartinezbu), [@munozariasjm](https://github.com/munozariasjm), [@fordaz](https://github.com/fordaz) |
+| [Persian](https://huggingface.co/course/fa/chapter1/1) (WIP) | [@jowharshamshiri](https://github.com/jowharshamshiri), [@schoobani](https://github.com/schoobani) |
+| [Gujarati](https://huggingface.co/course/gu/chapter1/1) (WIP) | [@pandyaved98](https://github.com/pandyaved98) |
+| [Hebrew](https://huggingface.co/course/he/chapter1/1) (WIP) | [@omer-dor](https://github.com/omer-dor) |
+| [Hindi](https://huggingface.co/course/hi/chapter1/1) (WIP) | [@pandyaved98](https://github.com/pandyaved98) |
+| [Bahasa Indonesia](https://huggingface.co/course/id/chapter1/1) (WIP) | [@gstdl](https://github.com/gstdl) |
+| [Italian](https://huggingface.co/course/it/chapter1/1) (WIP) | [@CaterinaBi](https://github.com/CaterinaBi), [@ClonedOne](https://github.com/ClonedOne), [@Nolanogenn](https://github.com/Nolanogenn), [@EdAbati](https://github.com/EdAbati), [@gdacciaro](https://github.com/gdacciaro) |
+| [Japanese](https://huggingface.co/course/ja/chapter1/1) (WIP) | [@hiromu166](https://github.com/@hiromu166), [@younesbelkada](https://github.com/@younesbelkada), [@HiromuHota](https://github.com/@HiromuHota) |
+| [Korean](https://huggingface.co/course/ko/chapter1/1) (WIP) | [@Doohae](https://github.com/Doohae), [@wonhyeongseo](https://github.com/wonhyeongseo), [@dlfrnaos19](https://github.com/dlfrnaos19) |
+| [Portuguese](https://huggingface.co/course/pt/chapter1/1) (WIP) | [@johnnv1](https://github.com/johnnv1), [@victorescosta](https://github.com/victorescosta), [@LincolnVS](https://github.com/LincolnVS) |
+| [Russian](https://huggingface.co/course/ru/chapter1/1) (WIP) | [@pdumin](https://github.com/pdumin), [@svv73](https://github.com/svv73) |
+| [Thai](https://huggingface.co/course/th/chapter1/1) (WIP) | [@peeraponw](https://github.com/peeraponw), [@a-krirk](https://github.com/a-krirk), [@jomariya23156](https://github.com/jomariya23156), [@ckingkan](https://github.com/ckingkan) |
+| [Turkish](https://huggingface.co/course/tr/chapter1/1) (WIP) | [@tanersekmen](https://github.com/tanersekmen), [@mertbozkir](https://github.com/mertbozkir), [@ftarlaci](https://github.com/ftarlaci), [@akkasayaz](https://github.com/akkasayaz) |
+| [Chinese (traditional)](https://huggingface.co/course/zh-TW/chapter1/1) (WIP) | [@davidpeng86](https://github.com/davidpeng86) |
+
+For some languages, the [course YouTube videos](https://youtube.com/playlist?list=PLo2EIpI_JMQvWfQndUesu0nPBAtZ9gP1o) have subtitles in the language. You can enable them by first clicking the _CC_ button in the bottom right corner of the video. Then, under the settings icon ⚙️, you can select the language you want by selecting the _Subtitles/CC_ option.
+
+
+
+> [!TIP]
+> Don't see your language in the above table or you'd like to contribute to an existing translation? You can help us translate the course by following the instructions here.
+
+## Let's go 🚀
+
+Are you ready to roll? In this chapter, you will learn:
+
+* How to use the `pipeline()` function to solve NLP tasks such as text generation and classification
+* About the Transformer architecture
+* How to distinguish between encoder, decoder, and encoder-decoder architectures and use cases
+
diff --git a/chapters/en/chapter1/10.mdx b/chapters/en/chapter1/10.mdx
index b5d646a3c..15d6d4550 100644
--- a/chapters/en/chapter1/10.mdx
+++ b/chapters/en/chapter1/10.mdx
@@ -1,66 +1,66 @@
-# Summary[[summary]]
-
-
-
-In this chapter, you've been introduced to the fundamentals of Transformer models, Large Language Models (LLMs), and how they're revolutionizing AI and beyond.
-
-## Key concepts covered
-
-### Natural Language Processing and LLMs
-
-We explored what NLP is and how Large Language Models have transformed the field. You learned that:
-- NLP encompasses a wide range of tasks from classification to generation
-- LLMs are powerful models trained on massive amounts of text data
-- These models can perform multiple tasks within a single architecture
-- Despite their capabilities, LLMs have limitations including hallucinations and bias
-
-### Transformer capabilities
-
-You saw how the `pipeline()` function from 🤗 Transformers makes it easy to use pre-trained models for various tasks:
-- Text classification, token classification, and question answering
-- Text generation and summarization
-- Translation and other sequence-to-sequence tasks
-- Speech recognition and image classification
-
-### Transformer architecture
-
-We discussed how Transformer models work at a high level, including:
-- The importance of the attention mechanism
-- How transfer learning enables models to adapt to specific tasks
-- The three main architectural variants: encoder-only, decoder-only, and encoder-decoder
-
-### Model architectures and their applications
-A key aspect of this chapter was understanding which architecture to use for different tasks:
-
-| Model | Examples | Tasks |
-|-----------------|--------------------------------------------|----------------------------------------------------------------------------------|
-| Encoder-only | BERT, DistilBERT, ModernBERT | Sentence classification, named entity recognition, extractive question answering |
-| Decoder-only | GPT, LLaMA, Gemma, SmolLM | Text generation, conversational AI, creative writing |
-| Encoder-decoder | BART, T5, Marian, mBART | Summarization, translation, generative question answering |
-
-### Modern LLM developments
-You also learned about recent developments in the field:
-- How LLMs have grown in size and capability over time
-- The concept of scaling laws and how they guide model development
-- Specialized attention mechanisms that help models process longer sequences
-- The two-phase training approach of pretraining and instruction tuning
-
-### Practical applications
-Throughout the chapter, you've seen how these models can be applied to real-world problems:
-- Using the Hugging Face Hub to find and use pre-trained models
-- Leveraging the Inference API to test models directly in your browser
-- Understanding which models are best suited for specific tasks
-
-## Looking ahead
-
-Now that you have a solid understanding of what Transformer models are and how they work at a high level, you're ready to dive deeper into how to use them effectively. In the next chapters, you'll learn how to:
-
-- Use the Transformers library to load and fine-tune models
-- Process different types of data for model input
-- Adapt pre-trained models to your specific tasks
-- Deploy models for practical applications
-
-The foundation you've built in this chapter will serve you well as you explore more advanced topics and techniques in the coming sections.
+# Summary[[summary]]
+
+
+
+In this chapter, you've been introduced to the fundamentals of Transformer models, Large Language Models (LLMs), and how they're revolutionizing AI and beyond.
+
+## Key concepts covered
+
+### Natural Language Processing and LLMs
+
+We explored what NLP is and how Large Language Models have transformed the field. You learned that:
+- NLP encompasses a wide range of tasks from classification to generation
+- LLMs are powerful models trained on massive amounts of text data
+- These models can perform multiple tasks within a single architecture
+- Despite their capabilities, LLMs have limitations including hallucinations and bias
+
+### Transformer capabilities
+
+You saw how the `pipeline()` function from 🤗 Transformers makes it easy to use pre-trained models for various tasks:
+- Text classification, token classification, and question answering
+- Text generation and summarization
+- Translation and other sequence-to-sequence tasks
+- Speech recognition and image classification
+
+### Transformer architecture
+
+We discussed how Transformer models work at a high level, including:
+- The importance of the attention mechanism
+- How transfer learning enables models to adapt to specific tasks
+- The three main architectural variants: encoder-only, decoder-only, and encoder-decoder
+
+### Model architectures and their applications
+A key aspect of this chapter was understanding which architecture to use for different tasks:
+
+| Model | Examples | Tasks |
+|-----------------|--------------------------------------------|----------------------------------------------------------------------------------|
+| Encoder-only | BERT, DistilBERT, ModernBERT | Sentence classification, named entity recognition, extractive question answering |
+| Decoder-only | GPT, LLaMA, Gemma, SmolLM | Text generation, conversational AI, creative writing |
+| Encoder-decoder | BART, T5, Marian, mBART | Summarization, translation, generative question answering |
+
+### Modern LLM developments
+You also learned about recent developments in the field:
+- How LLMs have grown in size and capability over time
+- The concept of scaling laws and how they guide model development
+- Specialized attention mechanisms that help models process longer sequences
+- The two-phase training approach of pretraining and instruction tuning
+
+### Practical applications
+Throughout the chapter, you've seen how these models can be applied to real-world problems:
+- Using the Hugging Face Hub to find and use pre-trained models
+- Leveraging the Inference API to test models directly in your browser
+- Understanding which models are best suited for specific tasks
+
+## Looking ahead
+
+Now that you have a solid understanding of what Transformer models are and how they work at a high level, you're ready to dive deeper into how to use them effectively. In the next chapters, you'll learn how to:
+
+- Use the Transformers library to load and fine-tune models
+- Process different types of data for model input
+- Adapt pre-trained models to your specific tasks
+- Deploy models for practical applications
+
+The foundation you've built in this chapter will serve you well as you explore more advanced topics and techniques in the coming sections.
diff --git a/chapters/en/chapter1/11.mdx b/chapters/en/chapter1/11.mdx
index dddddf271..1c2b7bc9c 100644
--- a/chapters/en/chapter1/11.mdx
+++ b/chapters/en/chapter1/11.mdx
@@ -1,21 +1,21 @@
-# Exam Time!
-
-It's time to put your knowledge to the test! We've prepared a short quiz for you to test your understanding of the concepts covered in this chapter.
-
-To take the quiz, you will need to follow these steps:
-
-1. Sign in to your Hugging Face account.
-2. Answer the questions in the quiz.
-3. Submit your answers.
-
-
-## Multiple Choice Quiz
-
-In this quiz, you will be asked to select the correct answer from a list of options. We'll test you on the fundamentals of supervised finetuning.
-
-
+# Exam Time!
+
+It's time to put your knowledge to the test! We've prepared a short quiz for you to test your understanding of the concepts covered in this chapter.
+
+To take the quiz, you will need to follow these steps:
+
+1. Sign in to your Hugging Face account.
+2. Answer the questions in the quiz.
+3. Submit your answers.
+
+
+## Multiple Choice Quiz
+
+In this quiz, you will be asked to select the correct answer from a list of options. We'll test you on the fundamentals of supervised finetuning.
+
+
diff --git a/chapters/en/chapter1/2.mdx b/chapters/en/chapter1/2.mdx
index ebfb179a4..decda3f96 100644
--- a/chapters/en/chapter1/2.mdx
+++ b/chapters/en/chapter1/2.mdx
@@ -1,52 +1,52 @@
-# Natural Language Processing and Large Language Models[[natural-language-processing-and-large-language-models]]
-
-
-
-Before jumping into Transformer models, let's do a quick overview of what natural language processing is, how large language models have transformed the field, and why we care about it.
-
-## What is NLP?[[what-is-nlp]]
-
-
-
-NLP is a field of linguistics and machine learning focused on understanding everything related to human language. The aim of NLP tasks is not only to understand single words individually, but to be able to understand the context of those words.
-
-The following is a list of common NLP tasks, with some examples of each:
-
-- **Classifying whole sentences**: Getting the sentiment of a review, detecting if an email is spam, determining if a sentence is grammatically correct or whether two sentences are logically related or not
-- **Classifying each word in a sentence**: Identifying the grammatical components of a sentence (noun, verb, adjective), or the named entities (person, location, organization)
-- **Generating text content**: Completing a prompt with auto-generated text, filling in the blanks in a text with masked words
-- **Extracting an answer from a text**: Given a question and a context, extracting the answer to the question based on the information provided in the context
-- **Generating a new sentence from an input text**: Translating a text into another language, summarizing a text
-
-NLP isn't limited to written text though. It also tackles complex challenges in speech recognition and computer vision, such as generating a transcript of an audio sample or a description of an image.
-
-## The Rise of Large Language Models (LLMs)[[rise-of-llms]]
-
-In recent years, the field of NLP has been revolutionized by Large Language Models (LLMs). These models, which include architectures like GPT (Generative Pre-trained Transformer) and [Llama](https://huggingface.co/meta-llama), have transformed what's possible in language processing.
-
-> [!TIP]
-> A large language model (LLM) is an AI model trained on massive amounts of text data that can understand and generate human-like text, recognize patterns in language, and perform a wide variety of language tasks without task-specific training. They represent a significant advancement in the field of natural language processing (NLP).
-
-LLMs are characterized by:
-- **Scale**: They contain millions, billions, or even hundreds of billions of parameters
-- **General capabilities**: They can perform multiple tasks without task-specific training
-- **In-context learning**: They can learn from examples provided in the prompt
-- **Emergent abilities**: As these models grow in size, they demonstrate capabilities that weren't explicitly programmed or anticipated
-
-The advent of LLMs has shifted the paradigm from building specialized models for specific NLP tasks to using a single, large model that can be prompted or fine-tuned to address a wide range of language tasks. This has made sophisticated language processing more accessible while also introducing new challenges in areas like efficiency, ethics, and deployment.
-
-However, LLMs also have important limitations:
-- **Hallucinations**: They can generate incorrect information confidently
-- **Lack of true understanding**: They lack true understanding of the world and operate purely on statistical patterns
-- **Bias**: They may reproduce biases present in their training data or inputs.
-- **Context windows**: They have limited context windows (though this is improving)
-- **Computational resources**: They require significant computational resources
-
-## Why is language processing challenging?[[why-is-it-challenging]]
-
-Computers don't process information in the same way as humans. For example, when we read the sentence "I am hungry," we can easily understand its meaning. Similarly, given two sentences such as "I am hungry" and "I am sad," we're able to easily determine how similar they are. For machine learning (ML) models, such tasks are more difficult. The text needs to be processed in a way that enables the model to learn from it. And because language is complex, we need to think carefully about how this processing must be done. There has been a lot of research done on how to represent text, and we will look at some methods in the next chapter.
-
-Even with the advances in LLMs, many fundamental challenges remain. These include understanding ambiguity, cultural context, sarcasm, and humor. LLMs address these challenges through massive training on diverse datasets, but still often fall short of human-level understanding in many complex scenarios.
+# Natural Language Processing and Large Language Models[[natural-language-processing-and-large-language-models]]
+
+
+
+Before jumping into Transformer models, let's do a quick overview of what natural language processing is, how large language models have transformed the field, and why we care about it.
+
+## What is NLP?[[what-is-nlp]]
+
+
+
+NLP is a field of linguistics and machine learning focused on understanding everything related to human language. The aim of NLP tasks is not only to understand single words individually, but to be able to understand the context of those words.
+
+The following is a list of common NLP tasks, with some examples of each:
+
+- **Classifying whole sentences**: Getting the sentiment of a review, detecting if an email is spam, determining if a sentence is grammatically correct or whether two sentences are logically related or not
+- **Classifying each word in a sentence**: Identifying the grammatical components of a sentence (noun, verb, adjective), or the named entities (person, location, organization)
+- **Generating text content**: Completing a prompt with auto-generated text, filling in the blanks in a text with masked words
+- **Extracting an answer from a text**: Given a question and a context, extracting the answer to the question based on the information provided in the context
+- **Generating a new sentence from an input text**: Translating a text into another language, summarizing a text
+
+NLP isn't limited to written text though. It also tackles complex challenges in speech recognition and computer vision, such as generating a transcript of an audio sample or a description of an image.
+
+## The Rise of Large Language Models (LLMs)[[rise-of-llms]]
+
+In recent years, the field of NLP has been revolutionized by Large Language Models (LLMs). These models, which include architectures like GPT (Generative Pre-trained Transformer) and [Llama](https://huggingface.co/meta-llama), have transformed what's possible in language processing.
+
+> [!TIP]
+> A large language model (LLM) is an AI model trained on massive amounts of text data that can understand and generate human-like text, recognize patterns in language, and perform a wide variety of language tasks without task-specific training. They represent a significant advancement in the field of natural language processing (NLP).
+
+LLMs are characterized by:
+- **Scale**: They contain millions, billions, or even hundreds of billions of parameters
+- **General capabilities**: They can perform multiple tasks without task-specific training
+- **In-context learning**: They can learn from examples provided in the prompt
+- **Emergent abilities**: As these models grow in size, they demonstrate capabilities that weren't explicitly programmed or anticipated
+
+The advent of LLMs has shifted the paradigm from building specialized models for specific NLP tasks to using a single, large model that can be prompted or fine-tuned to address a wide range of language tasks. This has made sophisticated language processing more accessible while also introducing new challenges in areas like efficiency, ethics, and deployment.
+
+However, LLMs also have important limitations:
+- **Hallucinations**: They can generate incorrect information confidently
+- **Lack of true understanding**: They lack true understanding of the world and operate purely on statistical patterns
+- **Bias**: They may reproduce biases present in their training data or inputs.
+- **Context windows**: They have limited context windows (though this is improving)
+- **Computational resources**: They require significant computational resources
+
+## Why is language processing challenging?[[why-is-it-challenging]]
+
+Computers don't process information in the same way as humans. For example, when we read the sentence "I am hungry," we can easily understand its meaning. Similarly, given two sentences such as "I am hungry" and "I am sad," we're able to easily determine how similar they are. For machine learning (ML) models, such tasks are more difficult. The text needs to be processed in a way that enables the model to learn from it. And because language is complex, we need to think carefully about how this processing must be done. There has been a lot of research done on how to represent text, and we will look at some methods in the next chapter.
+
+Even with the advances in LLMs, many fundamental challenges remain. These include understanding ambiguity, cultural context, sarcasm, and humor. LLMs address these challenges through massive training on diverse datasets, but still often fall short of human-level understanding in many complex scenarios.
diff --git a/chapters/en/chapter1/3.mdx b/chapters/en/chapter1/3.mdx
index 7efc4ef8e..3f3762356 100644
--- a/chapters/en/chapter1/3.mdx
+++ b/chapters/en/chapter1/3.mdx
@@ -269,8 +269,7 @@ Summarization is the task of reducing a text into a shorter text while keeping a
from transformers import pipeline
summarizer = pipeline("summarization")
-summarizer(
- """
+summarizer("""
America has changed dramatically during recent years. Not only has the number of
graduates in traditional engineering disciplines such as mechanical, civil,
electrical, chemical, and aeronautical engineering declined, but in most of
@@ -289,8 +288,7 @@ summarizer(
Other industrial countries at minimum maintain their output, while America
suffers an increasingly serious decline in the number of engineering graduates
and a lack of well-educated engineers.
-"""
-)
+""")
```
```python out
diff --git a/chapters/en/chapter1/4.mdx b/chapters/en/chapter1/4.mdx
index 7500f6376..c9ec1d2d3 100644
--- a/chapters/en/chapter1/4.mdx
+++ b/chapters/en/chapter1/4.mdx
@@ -1,189 +1,189 @@
-# How do Transformers work?[[how-do-transformers-work]]
-
-
-
-In this section, we will take a look at the architecture of Transformer models and dive deeper into the concepts of attention, encoder-decoder architecture, and more.
-
-> [!WARNING]
-> 🚀 We're taking things up a notch here. This section is detailed and technical, so don't worry if you don't understand everything right away. We'll come back to these concepts later in the course.
-
-## A bit of Transformer history[[a-bit-of-transformer-history]]
-
-Here are some reference points in the (short) history of Transformer models:
-
-
-

-

-
-
-The [Transformer architecture](https://arxiv.org/abs/1706.03762) was introduced in June 2017. The focus of the original research was on translation tasks. This was followed by the introduction of several influential models, including:
-
-- **June 2018**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), the first pretrained Transformer model, used for fine-tuning on various NLP tasks and obtained state-of-the-art results
-
-- **October 2018**: [BERT](https://arxiv.org/abs/1810.04805), another large pretrained model, this one designed to produce better summaries of sentences (more on this in the next chapter!)
-
-- **February 2019**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), an improved (and bigger) version of GPT that was not immediately publicly released due to ethical concerns
-
-- **October 2019**: [T5](https://huggingface.co/papers/1910.10683), A multi-task focused implementation of the sequence-to-sequence Transformer architecture.
-
-- **May 2020**, [GPT-3](https://huggingface.co/papers/2005.14165), an even bigger version of GPT-2 that is able to perform well on a variety of tasks without the need for fine-tuning (called _zero-shot learning_)
-
-- **January 2022**: [InstructGPT](https://huggingface.co/papers/2203.02155), a version of GPT-3 that was trained to follow instructions better.
-
-- **January 2023**: [Llama](https://huggingface.co/papers/2302.13971), a large language model that is able to generate text in a variety of languages.
-
-- **March 2023**: [Mistral](https://huggingface.co/papers/2310.06825), a 7-billion-parameter language model that outperforms Llama 2 13B across all evaluated benchmarks, leveraging grouped-query attention for faster inference and sliding window attention to handle sequences of arbitrary length.
-
-- **May 2024**: [Gemma 2](https://huggingface.co/papers/2408.00118), a family of lightweight, state-of-the-art open models ranging from 2B to 27B parameters that incorporate interleaved local-global attentions and group-query attention, with smaller models trained using knowledge distillation to deliver performance competitive with models 2-3 times larger.
-
-- **November 2024**: [SmolLM2](https://huggingface.co/papers/2502.02737), a state-of-the-art small language model (135 million to 1.7 billion parameters) that achieves impressive performance despite its compact size, and unlocking new possibilities for mobile and edge devices.
-
-This list is far from comprehensive, and is just meant to highlight a few of the different kinds of Transformer models. Broadly, they can be grouped into three categories:
-
-- GPT-like (also called _auto-regressive_ Transformer models)
-- BERT-like (also called _auto-encoding_ Transformer models)
-- T5-like (also called _sequence-to-sequence_ Transformer models)
-
-We will dive into these families in more depth later on.
-
-## Transformers are language models[[transformers-are-language-models]]
-
-All the Transformer models mentioned above (GPT, BERT, T5, etc.) have been trained as *language models*. This means they have been trained on large amounts of raw text in a self-supervised fashion.
-
-Self-supervised learning is a type of training in which the objective is automatically computed from the inputs of the model. That means that humans are not needed to label the data!
-
-This type of model develops a statistical understanding of the language it has been trained on, but it's less useful for specific practical tasks. Because of this, the general pretrained model then goes through a process called *transfer learning* or *fine-tuning*. During this process, the model is fine-tuned in a supervised way -- that is, using human-annotated labels -- on a given task.
-
-An example of a task is predicting the next word in a sentence having read the *n* previous words. This is called *causal language modeling* because the output depends on the past and present inputs, but not the future ones.
-
-
-

-

-
-
-Another example is *masked language modeling*, in which the model predicts a masked word in the sentence.
-
-
-

-

-
-
-## Transformers are big models[[transformers-are-big-models]]
-
-Apart from a few outliers (like DistilBERT), the general strategy to achieve better performance is by increasing the models' sizes as well as the amount of data they are pretrained on.
-
-
-

-
-
-Unfortunately, training a model, especially a large one, requires a large amount of data. This becomes very costly in terms of time and compute resources. It even translates to environmental impact, as can be seen in the following graph.
-
-
-

-

-
-
-
-
-And this is showing a project for a (very big) model led by a team consciously trying to reduce the environmental impact of pretraining. The footprint of running lots of trials to get the best hyperparameters would be even higher.
-
-Imagine if each time a research team, a student organization, or a company wanted to train a model, it did so from scratch. This would lead to huge, unnecessary global costs!
-
-This is why sharing language models is paramount: sharing the trained weights and building on top of already trained weights reduces the overall compute cost and carbon footprint of the community.
-
-By the way, you can evaluate the carbon footprint of your models' training through several tools. For example [ML CO2 Impact](https://mlco2.github.io/impact/) or [Code Carbon]( https://codecarbon.io/) which is integrated in 🤗 Transformers. To learn more about this, you can read this [blog post](https://huggingface.co/blog/carbon-emissions-on-the-hub) which will show you how to generate an `emissions.csv` file with an estimate of the footprint of your training, as well as the [documentation](https://huggingface.co/docs/hub/model-cards-co2) of 🤗 Transformers addressing this topic.
-
-
-## Transfer Learning[[transfer-learning]]
-
-
-
-*Pretraining* is the act of training a model from scratch: the weights are randomly initialized, and the training starts without any prior knowledge.
-
-
-

-

-
-
-This pretraining is usually done on very large amounts of data. Therefore, it requires a very large corpus of data, and training can take up to several weeks.
-
-*Fine-tuning*, on the other hand, is the training done **after** a model has been pretrained. To perform fine-tuning, you first acquire a pretrained language model, then perform additional training with a dataset specific to your task. Wait -- why not simply train the model for your final use case from the start (**scratch**)? There are a couple of reasons:
-
-* The pretrained model was already trained on a dataset that has some similarities with the fine-tuning dataset. The fine-tuning process is thus able to take advantage of knowledge acquired by the initial model during pretraining (for instance, with NLP problems, the pretrained model will have some kind of statistical understanding of the language you are using for your task).
-* Since the pretrained model was already trained on lots of data, the fine-tuning requires way less data to get decent results.
-* For the same reason, the amount of time and resources needed to get good results are much lower.
-
-For example, one could leverage a pretrained model trained on the English language and then fine-tune it on an arXiv corpus, resulting in a science/research-based model. The fine-tuning will only require a limited amount of data: the knowledge the pretrained model has acquired is "transferred," hence the term *transfer learning*.
-
-
-

-

-
-
-Fine-tuning a model therefore has lower time, data, financial, and environmental costs. It is also quicker and easier to iterate over different fine-tuning schemes, as the training is less constraining than a full pretraining.
-
-This process will also achieve better results than training from scratch (unless you have lots of data), which is why you should always try to leverage a pretrained model -- one as close as possible to the task you have at hand -- and fine-tune it.
-
-## General Transformer architecture[[general-transformer-architecture]]
-
-In this section, we'll go over the general architecture of the Transformer model. Don't worry if you don't understand some of the concepts; there are detailed sections later covering each of the components.
-
-
-
-The model is primarily composed of two blocks:
-
-* **Encoder (left)**: The encoder receives an input and builds a representation of it (its features). This means that the model is optimized to acquire understanding from the input.
-* **Decoder (right)**: The decoder uses the encoder's representation (features) along with other inputs to generate a target sequence. This means that the model is optimized for generating outputs.
-
-
-

-

-
-
-Each of these parts can be used independently, depending on the task:
-
-* **Encoder-only models**: Good for tasks that require understanding of the input, such as sentence classification and named entity recognition.
-* **Decoder-only models**: Good for generative tasks such as text generation.
-* **Encoder-decoder models** or **sequence-to-sequence models**: Good for generative tasks that require an input, such as translation or summarization.
-
-We will dive into those architectures independently in later sections.
-
-## Attention layers[[attention-layers]]
-
-A key feature of Transformer models is that they are built with special layers called *attention layers*. In fact, the title of the paper introducing the Transformer architecture was ["Attention Is All You Need"](https://arxiv.org/abs/1706.03762)! We will explore the details of attention layers later in the course; for now, all you need to know is that this layer will tell the model to pay specific attention to certain words in the sentence you passed it (and more or less ignore the others) when dealing with the representation of each word.
-
-To put this into context, consider the task of translating text from English to French. Given the input "You like this course", a translation model will need to also attend to the adjacent word "You" to get the proper translation for the word "like", because in French the verb "like" is conjugated differently depending on the subject. The rest of the sentence, however, is not useful for the translation of that word. In the same vein, when translating "this" the model will also need to pay attention to the word "course", because "this" translates differently depending on whether the associated noun is masculine or feminine. Again, the other words in the sentence will not matter for the translation of "course". With more complex sentences (and more complex grammar rules), the model would need to pay special attention to words that might appear farther away in the sentence to properly translate each word.
-
-The same concept applies to any task associated with natural language: a word by itself has a meaning, but that meaning is deeply affected by the context, which can be any other word (or words) before or after the word being studied.
-
-Now that you have an idea of what attention layers are all about, let's take a closer look at the Transformer architecture.
-
-## The original architecture[[the-original-architecture]]
-
-The Transformer architecture was originally designed for translation. During training, the encoder receives inputs (sentences) in a certain language, while the decoder receives the same sentences in the desired target language. In the encoder, the attention layers can use all the words in a sentence (since, as we just saw, the translation of a given word can be dependent on what is after as well as before it in the sentence). The decoder, however, works sequentially and can only pay attention to the words in the sentence that it has already translated (so, only the words before the word currently being generated). For example, when we have predicted the first three words of the translated target, we give them to the decoder which then uses all the inputs of the encoder to try to predict the fourth word.
-
-To speed things up during training (when the model has access to target sentences), the decoder is fed the whole target, but it is not allowed to use future words (if it had access to the word at position 2 when trying to predict the word at position 2, the problem would not be very hard!). For instance, when trying to predict the fourth word, the attention layer will only have access to the words in positions 1 to 3.
-
-The original Transformer architecture looked like this, with the encoder on the left and the decoder on the right:
-
-
-

-

-
-
-Note that the first attention layer in a decoder block pays attention to all (past) inputs to the decoder, but the second attention layer uses the output of the encoder. It can thus access the whole input sentence to best predict the current word. This is very useful as different languages can have grammatical rules that put the words in different orders, or some context provided later in the sentence may be helpful to determine the best translation of a given word.
-
-The *attention mask* can also be used in the encoder/decoder to prevent the model from paying attention to some special words -- for instance, the special padding word used to make all the inputs the same length when batching together sentences.
-
-## Architectures vs. checkpoints[[architecture-vs-checkpoints]]
-
-As we dive into Transformer models in this course, you'll see mentions of *architectures* and *checkpoints* as well as *models*. These terms all have slightly different meanings:
-
-* **Architecture**: This is the skeleton of the model -- the definition of each layer and each operation that happens within the model.
-* **Checkpoints**: These are the weights that will be loaded in a given architecture.
-* **Model**: This is an umbrella term that isn't as precise as "architecture" or "checkpoint": it can mean both. This course will specify *architecture* or *checkpoint* when it matters to reduce ambiguity.
-
-For example, BERT is an architecture while `bert-base-cased`, a set of weights trained by the Google team for the first release of BERT, is a checkpoint. However, one can say "the BERT model" and "the `bert-base-cased` model."
+# How do Transformers work?[[how-do-transformers-work]]
+
+
+
+In this section, we will take a look at the architecture of Transformer models and dive deeper into the concepts of attention, encoder-decoder architecture, and more.
+
+> [!WARNING]
+> 🚀 We're taking things up a notch here. This section is detailed and technical, so don't worry if you don't understand everything right away. We'll come back to these concepts later in the course.
+
+## A bit of Transformer history[[a-bit-of-transformer-history]]
+
+Here are some reference points in the (short) history of Transformer models:
+
+
+

+

+
+
+The [Transformer architecture](https://arxiv.org/abs/1706.03762) was introduced in June 2017. The focus of the original research was on translation tasks. This was followed by the introduction of several influential models, including:
+
+- **June 2018**: [GPT](https://cdn.openai.com/research-covers/language-unsupervised/language_understanding_paper.pdf), the first pretrained Transformer model, used for fine-tuning on various NLP tasks and obtained state-of-the-art results
+
+- **October 2018**: [BERT](https://arxiv.org/abs/1810.04805), another large pretrained model, this one designed to produce better summaries of sentences (more on this in the next chapter!)
+
+- **February 2019**: [GPT-2](https://cdn.openai.com/better-language-models/language_models_are_unsupervised_multitask_learners.pdf), an improved (and bigger) version of GPT that was not immediately publicly released due to ethical concerns
+
+- **October 2019**: [T5](https://huggingface.co/papers/1910.10683), A multi-task focused implementation of the sequence-to-sequence Transformer architecture.
+
+- **May 2020**, [GPT-3](https://huggingface.co/papers/2005.14165), an even bigger version of GPT-2 that is able to perform well on a variety of tasks without the need for fine-tuning (called _zero-shot learning_)
+
+- **January 2022**: [InstructGPT](https://huggingface.co/papers/2203.02155), a version of GPT-3 that was trained to follow instructions better.
+
+- **January 2023**: [Llama](https://huggingface.co/papers/2302.13971), a large language model that is able to generate text in a variety of languages.
+
+- **March 2023**: [Mistral](https://huggingface.co/papers/2310.06825), a 7-billion-parameter language model that outperforms Llama 2 13B across all evaluated benchmarks, leveraging grouped-query attention for faster inference and sliding window attention to handle sequences of arbitrary length.
+
+- **May 2024**: [Gemma 2](https://huggingface.co/papers/2408.00118), a family of lightweight, state-of-the-art open models ranging from 2B to 27B parameters that incorporate interleaved local-global attentions and group-query attention, with smaller models trained using knowledge distillation to deliver performance competitive with models 2-3 times larger.
+
+- **November 2024**: [SmolLM2](https://huggingface.co/papers/2502.02737), a state-of-the-art small language model (135 million to 1.7 billion parameters) that achieves impressive performance despite its compact size, and unlocking new possibilities for mobile and edge devices.
+
+This list is far from comprehensive, and is just meant to highlight a few of the different kinds of Transformer models. Broadly, they can be grouped into three categories:
+
+- GPT-like (also called _auto-regressive_ Transformer models)
+- BERT-like (also called _auto-encoding_ Transformer models)
+- T5-like (also called _sequence-to-sequence_ Transformer models)
+
+We will dive into these families in more depth later on.
+
+## Transformers are language models[[transformers-are-language-models]]
+
+All the Transformer models mentioned above (GPT, BERT, T5, etc.) have been trained as *language models*. This means they have been trained on large amounts of raw text in a self-supervised fashion.
+
+Self-supervised learning is a type of training in which the objective is automatically computed from the inputs of the model. That means that humans are not needed to label the data!
+
+This type of model develops a statistical understanding of the language it has been trained on, but it's less useful for specific practical tasks. Because of this, the general pretrained model then goes through a process called *transfer learning* or *fine-tuning*. During this process, the model is fine-tuned in a supervised way -- that is, using human-annotated labels -- on a given task.
+
+An example of a task is predicting the next word in a sentence having read the *n* previous words. This is called *causal language modeling* because the output depends on the past and present inputs, but not the future ones.
+
+
+

+

+
+
+Another example is *masked language modeling*, in which the model predicts a masked word in the sentence.
+
+
+

+

+
+
+## Transformers are big models[[transformers-are-big-models]]
+
+Apart from a few outliers (like DistilBERT), the general strategy to achieve better performance is by increasing the models' sizes as well as the amount of data they are pretrained on.
+
+
+

+
+
+Unfortunately, training a model, especially a large one, requires a large amount of data. This becomes very costly in terms of time and compute resources. It even translates to environmental impact, as can be seen in the following graph.
+
+
+

+

+
+
+
+
+And this is showing a project for a (very big) model led by a team consciously trying to reduce the environmental impact of pretraining. The footprint of running lots of trials to get the best hyperparameters would be even higher.
+
+Imagine if each time a research team, a student organization, or a company wanted to train a model, it did so from scratch. This would lead to huge, unnecessary global costs!
+
+This is why sharing language models is paramount: sharing the trained weights and building on top of already trained weights reduces the overall compute cost and carbon footprint of the community.
+
+By the way, you can evaluate the carbon footprint of your models' training through several tools. For example [ML CO2 Impact](https://mlco2.github.io/impact/) or [Code Carbon]( https://codecarbon.io/) which is integrated in 🤗 Transformers. To learn more about this, you can read this [blog post](https://huggingface.co/blog/carbon-emissions-on-the-hub) which will show you how to generate an `emissions.csv` file with an estimate of the footprint of your training, as well as the [documentation](https://huggingface.co/docs/hub/model-cards-co2) of 🤗 Transformers addressing this topic.
+
+
+## Transfer Learning[[transfer-learning]]
+
+
+
+*Pretraining* is the act of training a model from scratch: the weights are randomly initialized, and the training starts without any prior knowledge.
+
+
+

+

+
+
+This pretraining is usually done on very large amounts of data. Therefore, it requires a very large corpus of data, and training can take up to several weeks.
+
+*Fine-tuning*, on the other hand, is the training done **after** a model has been pretrained. To perform fine-tuning, you first acquire a pretrained language model, then perform additional training with a dataset specific to your task. Wait -- why not simply train the model for your final use case from the start (**scratch**)? There are a couple of reasons:
+
+* The pretrained model was already trained on a dataset that has some similarities with the fine-tuning dataset. The fine-tuning process is thus able to take advantage of knowledge acquired by the initial model during pretraining (for instance, with NLP problems, the pretrained model will have some kind of statistical understanding of the language you are using for your task).
+* Since the pretrained model was already trained on lots of data, the fine-tuning requires way less data to get decent results.
+* For the same reason, the amount of time and resources needed to get good results are much lower.
+
+For example, one could leverage a pretrained model trained on the English language and then fine-tune it on an arXiv corpus, resulting in a science/research-based model. The fine-tuning will only require a limited amount of data: the knowledge the pretrained model has acquired is "transferred," hence the term *transfer learning*.
+
+
+

+

+
+
+Fine-tuning a model therefore has lower time, data, financial, and environmental costs. It is also quicker and easier to iterate over different fine-tuning schemes, as the training is less constraining than a full pretraining.
+
+This process will also achieve better results than training from scratch (unless you have lots of data), which is why you should always try to leverage a pretrained model -- one as close as possible to the task you have at hand -- and fine-tune it.
+
+## General Transformer architecture[[general-transformer-architecture]]
+
+In this section, we'll go over the general architecture of the Transformer model. Don't worry if you don't understand some of the concepts; there are detailed sections later covering each of the components.
+
+
+
+The model is primarily composed of two blocks:
+
+* **Encoder (left)**: The encoder receives an input and builds a representation of it (its features). This means that the model is optimized to acquire understanding from the input.
+* **Decoder (right)**: The decoder uses the encoder's representation (features) along with other inputs to generate a target sequence. This means that the model is optimized for generating outputs.
+
+
+

+

+
+
+Each of these parts can be used independently, depending on the task:
+
+* **Encoder-only models**: Good for tasks that require understanding of the input, such as sentence classification and named entity recognition.
+* **Decoder-only models**: Good for generative tasks such as text generation.
+* **Encoder-decoder models** or **sequence-to-sequence models**: Good for generative tasks that require an input, such as translation or summarization.
+
+We will dive into those architectures independently in later sections.
+
+## Attention layers[[attention-layers]]
+
+A key feature of Transformer models is that they are built with special layers called *attention layers*. In fact, the title of the paper introducing the Transformer architecture was ["Attention Is All You Need"](https://arxiv.org/abs/1706.03762)! We will explore the details of attention layers later in the course; for now, all you need to know is that this layer will tell the model to pay specific attention to certain words in the sentence you passed it (and more or less ignore the others) when dealing with the representation of each word.
+
+To put this into context, consider the task of translating text from English to French. Given the input "You like this course", a translation model will need to also attend to the adjacent word "You" to get the proper translation for the word "like", because in French the verb "like" is conjugated differently depending on the subject. The rest of the sentence, however, is not useful for the translation of that word. In the same vein, when translating "this" the model will also need to pay attention to the word "course", because "this" translates differently depending on whether the associated noun is masculine or feminine. Again, the other words in the sentence will not matter for the translation of "course". With more complex sentences (and more complex grammar rules), the model would need to pay special attention to words that might appear farther away in the sentence to properly translate each word.
+
+The same concept applies to any task associated with natural language: a word by itself has a meaning, but that meaning is deeply affected by the context, which can be any other word (or words) before or after the word being studied.
+
+Now that you have an idea of what attention layers are all about, let's take a closer look at the Transformer architecture.
+
+## The original architecture[[the-original-architecture]]
+
+The Transformer architecture was originally designed for translation. During training, the encoder receives inputs (sentences) in a certain language, while the decoder receives the same sentences in the desired target language. In the encoder, the attention layers can use all the words in a sentence (since, as we just saw, the translation of a given word can be dependent on what is after as well as before it in the sentence). The decoder, however, works sequentially and can only pay attention to the words in the sentence that it has already translated (so, only the words before the word currently being generated). For example, when we have predicted the first three words of the translated target, we give them to the decoder which then uses all the inputs of the encoder to try to predict the fourth word.
+
+To speed things up during training (when the model has access to target sentences), the decoder is fed the whole target, but it is not allowed to use future words (if it had access to the word at position 2 when trying to predict the word at position 2, the problem would not be very hard!). For instance, when trying to predict the fourth word, the attention layer will only have access to the words in positions 1 to 3.
+
+The original Transformer architecture looked like this, with the encoder on the left and the decoder on the right:
+
+
+

+

+
+
+Note that the first attention layer in a decoder block pays attention to all (past) inputs to the decoder, but the second attention layer uses the output of the encoder. It can thus access the whole input sentence to best predict the current word. This is very useful as different languages can have grammatical rules that put the words in different orders, or some context provided later in the sentence may be helpful to determine the best translation of a given word.
+
+The *attention mask* can also be used in the encoder/decoder to prevent the model from paying attention to some special words -- for instance, the special padding word used to make all the inputs the same length when batching together sentences.
+
+## Architectures vs. checkpoints[[architecture-vs-checkpoints]]
+
+As we dive into Transformer models in this course, you'll see mentions of *architectures* and *checkpoints* as well as *models*. These terms all have slightly different meanings:
+
+* **Architecture**: This is the skeleton of the model -- the definition of each layer and each operation that happens within the model.
+* **Checkpoints**: These are the weights that will be loaded in a given architecture.
+* **Model**: This is an umbrella term that isn't as precise as "architecture" or "checkpoint": it can mean both. This course will specify *architecture* or *checkpoint* when it matters to reduce ambiguity.
+
+For example, BERT is an architecture while `bert-base-cased`, a set of weights trained by the Google team for the first release of BERT, is a checkpoint. However, one can say "the BERT model" and "the `bert-base-cased` model."
diff --git a/chapters/en/chapter1/5.mdx b/chapters/en/chapter1/5.mdx
index f00f6643b..d305202c6 100644
--- a/chapters/en/chapter1/5.mdx
+++ b/chapters/en/chapter1/5.mdx
@@ -1,235 +1,235 @@
-# How 🤗 Transformers solve tasks
-
-
-
-In [Transformers, what can they do?](/course/chapter1/3), you learned about natural language processing (NLP), speech and audio, computer vision tasks, and some important applications of them. This page will look closely at how models solve these tasks and explain what's happening under the hood. There are many ways to solve a given task, some models may implement certain techniques or even approach the task from a new angle, but for Transformer models, the general idea is the same. Owing to its flexible architecture, most models are a variant of an encoder, a decoder, or an encoder-decoder structure.
-
-> [!TIP]
-> Before diving into specific architectural variants, it's helpful to understand that most tasks follow a similar pattern: input data is processed through a model, and the output is interpreted for a specific task. The differences lie in how the data is prepared, what model architecture variant is used, and how the output is processed.
-
-To explain how tasks are solved, we'll walk through what goes on inside the model to output useful predictions. We'll cover the following models and their corresponding tasks:
-
-- [Wav2Vec2](https://huggingface.co/docs/transformers/model_doc/wav2vec2) for audio classification and automatic speech recognition (ASR)
-- [Vision Transformer (ViT)](https://huggingface.co/docs/transformers/model_doc/vit) and [ConvNeXT](https://huggingface.co/docs/transformers/model_doc/convnext) for image classification
-- [DETR](https://huggingface.co/docs/transformers/model_doc/detr) for object detection
-- [Mask2Former](https://huggingface.co/docs/transformers/model_doc/mask2former) for image segmentation
-- [GLPN](https://huggingface.co/docs/transformers/model_doc/glpn) for depth estimation
-- [BERT](https://huggingface.co/docs/transformers/model_doc/bert) for NLP tasks like text classification, token classification and question answering that use an encoder
-- [GPT2](https://huggingface.co/docs/transformers/model_doc/gpt2) for NLP tasks like text generation that use a decoder
-- [BART](https://huggingface.co/docs/transformers/model_doc/bart) for NLP tasks like summarization and translation that use an encoder-decoder
-
-> [!TIP]
-> Before you go further, it is good to have some basic knowledge of the original Transformer architecture. Knowing how encoders, decoders, and attention work will aid you in understanding how different Transformer models work. Be sure to check out our [the previous section](https://huggingface.co/course/chapter1/4?fw=pt) for more information!
-
-## Transformer models for language
-
-Language models are at the heart of modern NLP. They're designed to understand and generate human language by learning the statistical patterns and relationships between words or tokens in text.
-
-The Transformer was initially designed for machine translation, and since then, it has become the default architecture for solving all AI tasks. Some tasks lend themselves to the Transformer's encoder structure, while others are better suited for the decoder. Still, other tasks make use of both the Transformer's encoder-decoder structure.
-
-### How language models work
-
-Language models work by being trained to predict the probability of a word given the context of surrounding words. This gives them a foundational understanding of language that can generalize to other tasks.
-
-There are two main approaches for training a transformer model:
-
-1. **Masked language modeling (MLM)**: Used by encoder models like BERT, this approach randomly masks some tokens in the input and trains the model to predict the original tokens based on the surrounding context. This allows the model to learn bidirectional context (looking at words both before and after the masked word).
-
-2. **Causal language modeling (CLM)**: Used by decoder models like GPT, this approach predicts the next token based on all previous tokens in the sequence. The model can only use context from the left (previous tokens) to predict the next token.
-
-### Types of language models
-
-In the Transformers library, language models generally fall into three architectural categories:
-
-1. **Encoder-only models** (like BERT): These models use a bidirectional approach to understand context from both directions. They're best suited for tasks that require deep understanding of text, such as classification, named entity recognition, and question answering.
-
-2. **Decoder-only models** (like GPT, Llama): These models process text from left to right and are particularly good at text generation tasks. They can complete sentences, write essays, or even generate code based on a prompt.
-
-3. **Encoder-decoder models** (like T5, BART): These models combine both approaches, using an encoder to understand the input and a decoder to generate output. They excel at sequence-to-sequence tasks like translation, summarization, and question answering.
-
-
-
-As we covered in the previous section, language models are typically pretrained on large amounts of text data in a self-supervised manner (without human annotations), then fine-tuned on specific tasks. This approach, known as transfer learning, allows these models to adapt to many different NLP tasks with relatively small amounts of task-specific data.
-
-In the following sections, we'll explore specific model architectures and how they're applied to various tasks across speech, vision, and text domains.
-
-> [!TIP]
-> Understanding which part of the Transformer architecture (encoder, decoder, or both) is best suited for a particular NLP task is key to choosing the right model. Generally, tasks requiring bidirectional context use encoders, tasks generating text use decoders, and tasks converting one sequence to another use encoder-decoders.
-
-### Text generation
-
-Text generation involves creating coherent and contextually relevant text based on a prompt or input.
-
-[GPT-2](https://huggingface.co/docs/transformers/model_doc/gpt2) is a decoder-only model pretrained on a large amount of text. It can generate convincing (though not always true!) text given a prompt and complete other NLP tasks like question answering despite not being explicitly trained to.
-
-
-

-
-
-1. GPT-2 uses [byte pair encoding (BPE)](https://huggingface.co/docs/transformers/tokenizer_summary#bytepair-encoding-bpe) to tokenize words and generate a token embedding. Positional encodings are added to the token embeddings to indicate the position of each token in the sequence. The input embeddings are passed through multiple decoder blocks to output some final hidden state. Within each decoder block, GPT-2 uses a *masked self-attention* layer which means GPT-2 can't attend to future tokens. It is only allowed to attend to tokens on the left. This is different from BERT's [`mask`] token because, in masked self-attention, an attention mask is used to set the score to `0` for future tokens.
-
-2. The output from the decoder is passed to a language modeling head, which performs a linear transformation to convert the hidden states into logits. The label is the next token in the sequence, which are created by shifting the logits to the right by one. The cross-entropy loss is calculated between the shifted logits and the labels to output the next most likely token.
-
-GPT-2's pretraining objective is based entirely on [causal language modeling](https://huggingface.co/docs/transformers/glossary#causal-language-modeling), predicting the next word in a sequence. This makes GPT-2 especially good at tasks that involve generating text.
-
-Ready to try your hand at text generation? Check out our complete [causal language modeling guide](https://huggingface.co/docs/transformers/tasks/language_modeling#causal-language-modeling) to learn how to finetune DistilGPT-2 and use it for inference!
-
-> [!TIP]
-> For more information about text generation, check out the [text generation strategies](https://huggingface.co/docs/transformers/generation_strategies#generation-strategies) guide!
-
-### Text classification
-
-Text classification involves assigning predefined categories to text documents, such as sentiment analysis, topic classification, or spam detection.
-
-[BERT](https://huggingface.co/docs/transformers/model_doc/bert) is an encoder-only model and is the first model to effectively implement deep bidirectionality to learn richer representations of the text by attending to words on both sides.
-
-1. BERT uses [WordPiece](https://huggingface.co/docs/transformers/tokenizer_summary#wordpiece) tokenization to generate a token embedding of the text. To tell the difference between a single sentence and a pair of sentences, a special `[SEP]` token is added to differentiate them. A special `[CLS]` token is added to the beginning of every sequence of text. The final output with the `[CLS]` token is used as the input to the classification head for classification tasks. BERT also adds a segment embedding to denote whether a token belongs to the first or second sentence in a pair of sentences.
-
-2. BERT is pretrained with two objectives: masked language modeling and next-sentence prediction. In masked language modeling, some percentage of the input tokens are randomly masked, and the model needs to predict these. This solves the issue of bidirectionality, where the model could cheat and see all the words and "predict" the next word. The final hidden states of the predicted mask tokens are passed to a feedforward network with a softmax over the vocabulary to predict the masked word.
-
- The second pretraining object is next-sentence prediction. The model must predict whether sentence B follows sentence A. Half of the time sentence B is the next sentence, and the other half of the time, sentence B is a random sentence. The prediction, whether it is the next sentence or not, is passed to a feedforward network with a softmax over the two classes (`IsNext` and `NotNext`).
-
-3. The input embeddings are passed through multiple encoder layers to output some final hidden states.
-
-To use the pretrained model for text classification, add a sequence classification head on top of the base BERT model. The sequence classification head is a linear layer that accepts the final hidden states and performs a linear transformation to convert them into logits. The cross-entropy loss is calculated between the logits and target to find the most likely label.
-
-Ready to try your hand at text classification? Check out our complete [text classification guide](https://huggingface.co/docs/transformers/tasks/sequence_classification) to learn how to finetune DistilBERT and use it for inference!
-
-### Token classification
-
-Token classification involves assigning a label to each token in a sequence, such as in named entity recognition or part-of-speech tagging.
-
-To use BERT for token classification tasks like named entity recognition (NER), add a token classification head on top of the base BERT model. The token classification head is a linear layer that accepts the final hidden states and performs a linear transformation to convert them into logits. The cross-entropy loss is calculated between the logits and each token to find the most likely label.
-
-Ready to try your hand at token classification? Check out our complete [token classification guide](https://huggingface.co/docs/transformers/tasks/token_classification) to learn how to finetune DistilBERT and use it for inference!
-
-### Question answering
-
-Question answering involves finding the answer to a question within a given context or passage.
-
-To use BERT for question answering, add a span classification head on top of the base BERT model. This linear layer accepts the final hidden states and performs a linear transformation to compute the `span` start and end logits corresponding to the answer. The cross-entropy loss is calculated between the logits and the label position to find the most likely span of text corresponding to the answer.
-
-Ready to try your hand at question answering? Check out our complete [question answering guide](https://huggingface.co/docs/transformers/tasks/question_answering) to learn how to finetune DistilBERT and use it for inference!
-
-> [!TIP]
-> 💡 Notice how easy it is to use BERT for different tasks once it's been pretrained. You only need to add a specific head to the pretrained model to manipulate the hidden states into your desired output!
-
-### Summarization
-
-Summarization involves condensing a longer text into a shorter version while preserving its key information and meaning.
-
-Encoder-decoder models like [BART](https://huggingface.co/docs/transformers/model_doc/bart) and [T5](model_doc/t5) are designed for the sequence-to-sequence pattern of a summarization task. We'll explain how BART works in this section, and then you can try finetuning T5 at the end.
-
-
-

-
-
-1. BART's encoder architecture is very similar to BERT and accepts a token and positional embedding of the text. BART is pretrained by corrupting the input and then reconstructing it with the decoder. Unlike other encoders with specific corruption strategies, BART can apply any type of corruption. The *text infilling* corruption strategy works the best though. In text infilling, a number of text spans are replaced with a **single** [`mask`] token. This is important because the model has to predict the masked tokens, and it teaches the model to predict the number of missing tokens. The input embeddings and masked spans are passed through the encoder to output some final hidden states, but unlike BERT, BART doesn't add a final feedforward network at the end to predict a word.
-
-2. The encoder's output is passed to the decoder, which must predict the masked tokens and any uncorrupted tokens from the encoder's output. This gives additional context to help the decoder restore the original text. The output from the decoder is passed to a language modeling head, which performs a linear transformation to convert the hidden states into logits. The cross-entropy loss is calculated between the logits and the label, which is just the token shifted to the right.
-
-Ready to try your hand at summarization? Check out our complete [summarization guide](https://huggingface.co/docs/transformers/tasks/summarization) to learn how to finetune T5 and use it for inference!
-
-> [!TIP]
-> For more information about text generation, check out the [text generation strategies](https://huggingface.co/docs/transformers/generation_strategies) guide!
-
-### Translation
-
-Translation involves converting text from one language to another while preserving its meaning. Translation is another example of a sequence-to-sequence task, which means you can use an encoder-decoder model like [BART](https://huggingface.co/docs/transformers/model_doc/bart) or [T5](model_doc/t5) to do it. We'll explain how BART works in this section, and then you can try finetuning T5 at the end.
-
-BART adapts to translation by adding a separate randomly initialized encoder to map a source language to an input that can be decoded into the target language. This new encoder's embeddings are passed to the pretrained encoder instead of the original word embeddings. The source encoder is trained by updating the source encoder, positional embeddings, and input embeddings with the cross-entropy loss from the model output. The model parameters are frozen in this first step, and all the model parameters are trained together in the second step.
-BART has since been followed up by a multilingual version, mBART, intended for translation and pretrained on many different languages.
-
-Ready to try your hand at translation? Check out our complete [translation guide](https://huggingface.co/docs/transformers/tasks/translation) to learn how to finetune T5 and use it for inference!
-
-> [!TIP]
-> As you've seen throughout this guide, many models follow similar patterns despite addressing different tasks. Understanding these common patterns can help you quickly grasp how new models work and how to adapt existing models to your specific needs.
-
-## Modalities beyond text
-
-Transformers are not limited to text. They can also be applied to other modalities like speech and audio, images, and video. Of course, on this course we will focus on text, but we can briefly introduce the other modalities.
-
-### Speech and audio
-
-Let's start by exploring how Transformer models handle speech and audio data, which presents unique challenges compared to text or images.
-
-[Whisper](https://huggingface.co/docs/transformers/main/en/model_doc/whisper) is a encoder-decoder (sequence-to-sequence) transformer pretrained on 680,000 hours of labeled audio data. This amount of pretraining data enables zero-shot performance on audio tasks in English and many other languages. The decoder allows Whisper to map the encoders learned speech representations to useful outputs, such as text, without additional fine-tuning. Whisper just works out of the box.
-
-
-

-
-
-Diagram is from [Whisper paper](https://huggingface.co/papers/2212.04356).
-
-This model has two main components:
-
-1. An **encoder** processes the input audio. The raw audio is first converted into a log-Mel spectrogram. This spectrogram is then passed through a Transformer encoder network.
-
-2. A **decoder** takes the encoded audio representation and autoregressively predicts the corresponding text tokens. It's a standard Transformer decoder trained to predict the next text token given the previous tokens and the encoder output. Special tokens are used at the beginning of the decoder input to steer the model towards specific tasks like transcription, translation, or language identification.
-
-Whisper was pretrained on a massive and diverse dataset of 680,000 hours of labeled audio data collected from the web. This large-scale, weakly supervised pretraining is the key to its strong zero-shot performance across many languages and tasks.
-
-Now that Whisper is pretrained, you can use it directly for zero-shot inference or finetune it on your data for improved performance on specific tasks like automatic speech recognition or speech translation!
-
-> [!TIP]
-> The key innovation in Whisper is its training on an unprecedented scale of diverse, weakly supervised audio data from the internet. This allows it to generalize remarkably well to different languages, accents, and tasks without task-specific finetuning.
-
-### Automatic speech recognition
-
-To use the pretrained model for automatic speech recognition, you leverage its full encoder-decoder structure. The encoder processes the audio input, and the decoder autoregressively generates the transcript token by token. When fine-tuning, the model is typically trained using a standard sequence-to-sequence loss (like cross-entropy) to predict the correct text tokens based on the audio input.
-
-The easiest way to use a fine-tuned model for inference is within a `pipeline`.
-
-```python
-from transformers import pipeline
-
-transcriber = pipeline(
- task="automatic-speech-recognition", model="openai/whisper-base.en"
-)
-transcriber("https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac")
-# Output: {'text': ' I have a dream that one day this nation will rise up and live out the true meaning of its creed.'}
-```
-
-Ready to try your hand at automatic speech recognition? Check out our complete [automatic speech recognition guide](https://huggingface.co/docs/transformers/tasks/asr) to learn how to finetune Whisper and use it for inference!
-
-### Computer vision
-
-Now let's move on to computer vision tasks, which deal with understanding and interpreting visual information from images or videos.
-
-There are two ways to approach computer vision tasks:
-
-1. Split an image into a sequence of patches and process them in parallel with a Transformer.
-2. Use a modern CNN, like [ConvNeXT](https://huggingface.co/docs/transformers/model_doc/convnext), which relies on convolutional layers but adopts modern network designs.
-
-> [!TIP]
-> A third approach mixes Transformers with convolutions (for example, [Convolutional Vision Transformer](https://huggingface.co/docs/transformers/model_doc/cvt) or [LeViT](https://huggingface.co/docs/transformers/model_doc/levit)). We won't discuss those because they just combine the two approaches we examine here.
-
-ViT and ConvNeXT are commonly used for image classification, but for other vision tasks like object detection, segmentation, and depth estimation, we'll look at DETR, Mask2Former and GLPN, respectively; these models are better suited for those tasks.
-
-### Image classification
-
-Image classification is one of the fundamental computer vision tasks. Let's see how different model architectures approach this problem.
-
-ViT and ConvNeXT can both be used for image classification; the main difference is that ViT uses an attention mechanism while ConvNeXT uses convolutions.
-
-[ViT](https://huggingface.co/docs/transformers/model_doc/vit) replaces convolutions entirely with a pure Transformer architecture. If you're familiar with the original Transformer, then you're already most of the way toward understanding ViT.
-
-
-

-
-
-The main change ViT introduced was in how images are fed to a Transformer:
-
-1. An image is split into square non-overlapping patches, each of which gets turned into a vector or *patch embedding*. The patch embeddings are generated from a convolutional 2D layer which creates the proper input dimensions (which for a base Transformer is 768 values for each patch embedding). If you had a 224x224 pixel image, you could split it into 196 16x16 image patches. Just like how text is tokenized into words, an image is "tokenized" into a sequence of patches.
-
-2. A *learnable embedding* - a special `[CLS]` token - is added to the beginning of the patch embeddings just like BERT. The final hidden state of the `[CLS]` token is used as the input to the attached classification head; other outputs are ignored. This token helps the model learn how to encode a representation of the image.
-
-3. The last thing to add to the patch and learnable embeddings are the *position embeddings* because the model doesn't know how the image patches are ordered. The position embeddings are also learnable and have the same size as the patch embeddings. Finally, all of the embeddings are passed to the Transformer encoder.
-
-4. The output, specifically only the output with the `[CLS]` token, is passed to a multilayer perceptron head (MLP). ViT's pretraining objective is simply classification. Like other classification heads, the MLP head converts the output into logits over the class labels and calculates the cross-entropy loss to find the most likely class.
-
-Ready to try your hand at image classification? Check out our complete [image classification guide](https://huggingface.co/docs/transformers/tasks/image_classification) to learn how to fine-tune ViT and use it for inference!
-
-
-> [!TIP]
-> Notice the parallel between ViT and BERT: both use a special token ([CLS]) to capture the overall representation, both add position information to their embeddings, and both use a Transformer encoder to process the sequence of tokens/patches.
+# How 🤗 Transformers solve tasks
+
+
+
+In [Transformers, what can they do?](/course/chapter1/3), you learned about natural language processing (NLP), speech and audio, computer vision tasks, and some important applications of them. This page will look closely at how models solve these tasks and explain what's happening under the hood. There are many ways to solve a given task, some models may implement certain techniques or even approach the task from a new angle, but for Transformer models, the general idea is the same. Owing to its flexible architecture, most models are a variant of an encoder, a decoder, or an encoder-decoder structure.
+
+> [!TIP]
+> Before diving into specific architectural variants, it's helpful to understand that most tasks follow a similar pattern: input data is processed through a model, and the output is interpreted for a specific task. The differences lie in how the data is prepared, what model architecture variant is used, and how the output is processed.
+
+To explain how tasks are solved, we'll walk through what goes on inside the model to output useful predictions. We'll cover the following models and their corresponding tasks:
+
+- [Wav2Vec2](https://huggingface.co/docs/transformers/model_doc/wav2vec2) for audio classification and automatic speech recognition (ASR)
+- [Vision Transformer (ViT)](https://huggingface.co/docs/transformers/model_doc/vit) and [ConvNeXT](https://huggingface.co/docs/transformers/model_doc/convnext) for image classification
+- [DETR](https://huggingface.co/docs/transformers/model_doc/detr) for object detection
+- [Mask2Former](https://huggingface.co/docs/transformers/model_doc/mask2former) for image segmentation
+- [GLPN](https://huggingface.co/docs/transformers/model_doc/glpn) for depth estimation
+- [BERT](https://huggingface.co/docs/transformers/model_doc/bert) for NLP tasks like text classification, token classification and question answering that use an encoder
+- [GPT2](https://huggingface.co/docs/transformers/model_doc/gpt2) for NLP tasks like text generation that use a decoder
+- [BART](https://huggingface.co/docs/transformers/model_doc/bart) for NLP tasks like summarization and translation that use an encoder-decoder
+
+> [!TIP]
+> Before you go further, it is good to have some basic knowledge of the original Transformer architecture. Knowing how encoders, decoders, and attention work will aid you in understanding how different Transformer models work. Be sure to check out our [the previous section](https://huggingface.co/course/chapter1/4?fw=pt) for more information!
+
+## Transformer models for language
+
+Language models are at the heart of modern NLP. They're designed to understand and generate human language by learning the statistical patterns and relationships between words or tokens in text.
+
+The Transformer was initially designed for machine translation, and since then, it has become the default architecture for solving all AI tasks. Some tasks lend themselves to the Transformer's encoder structure, while others are better suited for the decoder. Still, other tasks make use of both the Transformer's encoder-decoder structure.
+
+### How language models work
+
+Language models work by being trained to predict the probability of a word given the context of surrounding words. This gives them a foundational understanding of language that can generalize to other tasks.
+
+There are two main approaches for training a transformer model:
+
+1. **Masked language modeling (MLM)**: Used by encoder models like BERT, this approach randomly masks some tokens in the input and trains the model to predict the original tokens based on the surrounding context. This allows the model to learn bidirectional context (looking at words both before and after the masked word).
+
+2. **Causal language modeling (CLM)**: Used by decoder models like GPT, this approach predicts the next token based on all previous tokens in the sequence. The model can only use context from the left (previous tokens) to predict the next token.
+
+### Types of language models
+
+In the Transformers library, language models generally fall into three architectural categories:
+
+1. **Encoder-only models** (like BERT): These models use a bidirectional approach to understand context from both directions. They're best suited for tasks that require deep understanding of text, such as classification, named entity recognition, and question answering.
+
+2. **Decoder-only models** (like GPT, Llama): These models process text from left to right and are particularly good at text generation tasks. They can complete sentences, write essays, or even generate code based on a prompt.
+
+3. **Encoder-decoder models** (like T5, BART): These models combine both approaches, using an encoder to understand the input and a decoder to generate output. They excel at sequence-to-sequence tasks like translation, summarization, and question answering.
+
+
+
+As we covered in the previous section, language models are typically pretrained on large amounts of text data in a self-supervised manner (without human annotations), then fine-tuned on specific tasks. This approach, known as transfer learning, allows these models to adapt to many different NLP tasks with relatively small amounts of task-specific data.
+
+In the following sections, we'll explore specific model architectures and how they're applied to various tasks across speech, vision, and text domains.
+
+> [!TIP]
+> Understanding which part of the Transformer architecture (encoder, decoder, or both) is best suited for a particular NLP task is key to choosing the right model. Generally, tasks requiring bidirectional context use encoders, tasks generating text use decoders, and tasks converting one sequence to another use encoder-decoders.
+
+### Text generation
+
+Text generation involves creating coherent and contextually relevant text based on a prompt or input.
+
+[GPT-2](https://huggingface.co/docs/transformers/model_doc/gpt2) is a decoder-only model pretrained on a large amount of text. It can generate convincing (though not always true!) text given a prompt and complete other NLP tasks like question answering despite not being explicitly trained to.
+
+
+

+
+
+1. GPT-2 uses [byte pair encoding (BPE)](https://huggingface.co/docs/transformers/tokenizer_summary#bytepair-encoding-bpe) to tokenize words and generate a token embedding. Positional encodings are added to the token embeddings to indicate the position of each token in the sequence. The input embeddings are passed through multiple decoder blocks to output some final hidden state. Within each decoder block, GPT-2 uses a *masked self-attention* layer which means GPT-2 can't attend to future tokens. It is only allowed to attend to tokens on the left. This is different from BERT's [`mask`] token because, in masked self-attention, an attention mask is used to set the score to `0` for future tokens.
+
+2. The output from the decoder is passed to a language modeling head, which performs a linear transformation to convert the hidden states into logits. The label is the next token in the sequence, which are created by shifting the logits to the right by one. The cross-entropy loss is calculated between the shifted logits and the labels to output the next most likely token.
+
+GPT-2's pretraining objective is based entirely on [causal language modeling](https://huggingface.co/docs/transformers/glossary#causal-language-modeling), predicting the next word in a sequence. This makes GPT-2 especially good at tasks that involve generating text.
+
+Ready to try your hand at text generation? Check out our complete [causal language modeling guide](https://huggingface.co/docs/transformers/tasks/language_modeling#causal-language-modeling) to learn how to finetune DistilGPT-2 and use it for inference!
+
+> [!TIP]
+> For more information about text generation, check out the [text generation strategies](https://huggingface.co/docs/transformers/generation_strategies#generation-strategies) guide!
+
+### Text classification
+
+Text classification involves assigning predefined categories to text documents, such as sentiment analysis, topic classification, or spam detection.
+
+[BERT](https://huggingface.co/docs/transformers/model_doc/bert) is an encoder-only model and is the first model to effectively implement deep bidirectionality to learn richer representations of the text by attending to words on both sides.
+
+1. BERT uses [WordPiece](https://huggingface.co/docs/transformers/tokenizer_summary#wordpiece) tokenization to generate a token embedding of the text. To tell the difference between a single sentence and a pair of sentences, a special `[SEP]` token is added to differentiate them. A special `[CLS]` token is added to the beginning of every sequence of text. The final output with the `[CLS]` token is used as the input to the classification head for classification tasks. BERT also adds a segment embedding to denote whether a token belongs to the first or second sentence in a pair of sentences.
+
+2. BERT is pretrained with two objectives: masked language modeling and next-sentence prediction. In masked language modeling, some percentage of the input tokens are randomly masked, and the model needs to predict these. This solves the issue of bidirectionality, where the model could cheat and see all the words and "predict" the next word. The final hidden states of the predicted mask tokens are passed to a feedforward network with a softmax over the vocabulary to predict the masked word.
+
+ The second pretraining object is next-sentence prediction. The model must predict whether sentence B follows sentence A. Half of the time sentence B is the next sentence, and the other half of the time, sentence B is a random sentence. The prediction, whether it is the next sentence or not, is passed to a feedforward network with a softmax over the two classes (`IsNext` and `NotNext`).
+
+3. The input embeddings are passed through multiple encoder layers to output some final hidden states.
+
+To use the pretrained model for text classification, add a sequence classification head on top of the base BERT model. The sequence classification head is a linear layer that accepts the final hidden states and performs a linear transformation to convert them into logits. The cross-entropy loss is calculated between the logits and target to find the most likely label.
+
+Ready to try your hand at text classification? Check out our complete [text classification guide](https://huggingface.co/docs/transformers/tasks/sequence_classification) to learn how to finetune DistilBERT and use it for inference!
+
+### Token classification
+
+Token classification involves assigning a label to each token in a sequence, such as in named entity recognition or part-of-speech tagging.
+
+To use BERT for token classification tasks like named entity recognition (NER), add a token classification head on top of the base BERT model. The token classification head is a linear layer that accepts the final hidden states and performs a linear transformation to convert them into logits. The cross-entropy loss is calculated between the logits and each token to find the most likely label.
+
+Ready to try your hand at token classification? Check out our complete [token classification guide](https://huggingface.co/docs/transformers/tasks/token_classification) to learn how to finetune DistilBERT and use it for inference!
+
+### Question answering
+
+Question answering involves finding the answer to a question within a given context or passage.
+
+To use BERT for question answering, add a span classification head on top of the base BERT model. This linear layer accepts the final hidden states and performs a linear transformation to compute the `span` start and end logits corresponding to the answer. The cross-entropy loss is calculated between the logits and the label position to find the most likely span of text corresponding to the answer.
+
+Ready to try your hand at question answering? Check out our complete [question answering guide](https://huggingface.co/docs/transformers/tasks/question_answering) to learn how to finetune DistilBERT and use it for inference!
+
+> [!TIP]
+> 💡 Notice how easy it is to use BERT for different tasks once it's been pretrained. You only need to add a specific head to the pretrained model to manipulate the hidden states into your desired output!
+
+### Summarization
+
+Summarization involves condensing a longer text into a shorter version while preserving its key information and meaning.
+
+Encoder-decoder models like [BART](https://huggingface.co/docs/transformers/model_doc/bart) and [T5](model_doc/t5) are designed for the sequence-to-sequence pattern of a summarization task. We'll explain how BART works in this section, and then you can try finetuning T5 at the end.
+
+
+

+
+
+1. BART's encoder architecture is very similar to BERT and accepts a token and positional embedding of the text. BART is pretrained by corrupting the input and then reconstructing it with the decoder. Unlike other encoders with specific corruption strategies, BART can apply any type of corruption. The *text infilling* corruption strategy works the best though. In text infilling, a number of text spans are replaced with a **single** [`mask`] token. This is important because the model has to predict the masked tokens, and it teaches the model to predict the number of missing tokens. The input embeddings and masked spans are passed through the encoder to output some final hidden states, but unlike BERT, BART doesn't add a final feedforward network at the end to predict a word.
+
+2. The encoder's output is passed to the decoder, which must predict the masked tokens and any uncorrupted tokens from the encoder's output. This gives additional context to help the decoder restore the original text. The output from the decoder is passed to a language modeling head, which performs a linear transformation to convert the hidden states into logits. The cross-entropy loss is calculated between the logits and the label, which is just the token shifted to the right.
+
+Ready to try your hand at summarization? Check out our complete [summarization guide](https://huggingface.co/docs/transformers/tasks/summarization) to learn how to finetune T5 and use it for inference!
+
+> [!TIP]
+> For more information about text generation, check out the [text generation strategies](https://huggingface.co/docs/transformers/generation_strategies) guide!
+
+### Translation
+
+Translation involves converting text from one language to another while preserving its meaning. Translation is another example of a sequence-to-sequence task, which means you can use an encoder-decoder model like [BART](https://huggingface.co/docs/transformers/model_doc/bart) or [T5](model_doc/t5) to do it. We'll explain how BART works in this section, and then you can try finetuning T5 at the end.
+
+BART adapts to translation by adding a separate randomly initialized encoder to map a source language to an input that can be decoded into the target language. This new encoder's embeddings are passed to the pretrained encoder instead of the original word embeddings. The source encoder is trained by updating the source encoder, positional embeddings, and input embeddings with the cross-entropy loss from the model output. The model parameters are frozen in this first step, and all the model parameters are trained together in the second step.
+BART has since been followed up by a multilingual version, mBART, intended for translation and pretrained on many different languages.
+
+Ready to try your hand at translation? Check out our complete [translation guide](https://huggingface.co/docs/transformers/tasks/translation) to learn how to finetune T5 and use it for inference!
+
+> [!TIP]
+> As you've seen throughout this guide, many models follow similar patterns despite addressing different tasks. Understanding these common patterns can help you quickly grasp how new models work and how to adapt existing models to your specific needs.
+
+## Modalities beyond text
+
+Transformers are not limited to text. They can also be applied to other modalities like speech and audio, images, and video. Of course, on this course we will focus on text, but we can briefly introduce the other modalities.
+
+### Speech and audio
+
+Let's start by exploring how Transformer models handle speech and audio data, which presents unique challenges compared to text or images.
+
+[Whisper](https://huggingface.co/docs/transformers/main/en/model_doc/whisper) is a encoder-decoder (sequence-to-sequence) transformer pretrained on 680,000 hours of labeled audio data. This amount of pretraining data enables zero-shot performance on audio tasks in English and many other languages. The decoder allows Whisper to map the encoders learned speech representations to useful outputs, such as text, without additional fine-tuning. Whisper just works out of the box.
+
+
+

+
+
+Diagram is from [Whisper paper](https://huggingface.co/papers/2212.04356).
+
+This model has two main components:
+
+1. An **encoder** processes the input audio. The raw audio is first converted into a log-Mel spectrogram. This spectrogram is then passed through a Transformer encoder network.
+
+2. A **decoder** takes the encoded audio representation and autoregressively predicts the corresponding text tokens. It's a standard Transformer decoder trained to predict the next text token given the previous tokens and the encoder output. Special tokens are used at the beginning of the decoder input to steer the model towards specific tasks like transcription, translation, or language identification.
+
+Whisper was pretrained on a massive and diverse dataset of 680,000 hours of labeled audio data collected from the web. This large-scale, weakly supervised pretraining is the key to its strong zero-shot performance across many languages and tasks.
+
+Now that Whisper is pretrained, you can use it directly for zero-shot inference or finetune it on your data for improved performance on specific tasks like automatic speech recognition or speech translation!
+
+> [!TIP]
+> The key innovation in Whisper is its training on an unprecedented scale of diverse, weakly supervised audio data from the internet. This allows it to generalize remarkably well to different languages, accents, and tasks without task-specific finetuning.
+
+### Automatic speech recognition
+
+To use the pretrained model for automatic speech recognition, you leverage its full encoder-decoder structure. The encoder processes the audio input, and the decoder autoregressively generates the transcript token by token. When fine-tuning, the model is typically trained using a standard sequence-to-sequence loss (like cross-entropy) to predict the correct text tokens based on the audio input.
+
+The easiest way to use a fine-tuned model for inference is within a `pipeline`.
+
+```python
+from transformers import pipeline
+
+transcriber = pipeline(
+ task="automatic-speech-recognition", model="openai/whisper-base.en"
+)
+transcriber("https://huggingface.co/datasets/Narsil/asr_dummy/resolve/main/mlk.flac")
+# Output: {'text': ' I have a dream that one day this nation will rise up and live out the true meaning of its creed.'}
+```
+
+Ready to try your hand at automatic speech recognition? Check out our complete [automatic speech recognition guide](https://huggingface.co/docs/transformers/tasks/asr) to learn how to finetune Whisper and use it for inference!
+
+### Computer vision
+
+Now let's move on to computer vision tasks, which deal with understanding and interpreting visual information from images or videos.
+
+There are two ways to approach computer vision tasks:
+
+1. Split an image into a sequence of patches and process them in parallel with a Transformer.
+2. Use a modern CNN, like [ConvNeXT](https://huggingface.co/docs/transformers/model_doc/convnext), which relies on convolutional layers but adopts modern network designs.
+
+> [!TIP]
+> A third approach mixes Transformers with convolutions (for example, [Convolutional Vision Transformer](https://huggingface.co/docs/transformers/model_doc/cvt) or [LeViT](https://huggingface.co/docs/transformers/model_doc/levit)). We won't discuss those because they just combine the two approaches we examine here.
+
+ViT and ConvNeXT are commonly used for image classification, but for other vision tasks like object detection, segmentation, and depth estimation, we'll look at DETR, Mask2Former and GLPN, respectively; these models are better suited for those tasks.
+
+### Image classification
+
+Image classification is one of the fundamental computer vision tasks. Let's see how different model architectures approach this problem.
+
+ViT and ConvNeXT can both be used for image classification; the main difference is that ViT uses an attention mechanism while ConvNeXT uses convolutions.
+
+[ViT](https://huggingface.co/docs/transformers/model_doc/vit) replaces convolutions entirely with a pure Transformer architecture. If you're familiar with the original Transformer, then you're already most of the way toward understanding ViT.
+
+
+

+
+
+The main change ViT introduced was in how images are fed to a Transformer:
+
+1. An image is split into square non-overlapping patches, each of which gets turned into a vector or *patch embedding*. The patch embeddings are generated from a convolutional 2D layer which creates the proper input dimensions (which for a base Transformer is 768 values for each patch embedding). If you had a 224x224 pixel image, you could split it into 196 16x16 image patches. Just like how text is tokenized into words, an image is "tokenized" into a sequence of patches.
+
+2. A *learnable embedding* - a special `[CLS]` token - is added to the beginning of the patch embeddings just like BERT. The final hidden state of the `[CLS]` token is used as the input to the attached classification head; other outputs are ignored. This token helps the model learn how to encode a representation of the image.
+
+3. The last thing to add to the patch and learnable embeddings are the *position embeddings* because the model doesn't know how the image patches are ordered. The position embeddings are also learnable and have the same size as the patch embeddings. Finally, all of the embeddings are passed to the Transformer encoder.
+
+4. The output, specifically only the output with the `[CLS]` token, is passed to a multilayer perceptron head (MLP). ViT's pretraining objective is simply classification. Like other classification heads, the MLP head converts the output into logits over the class labels and calculates the cross-entropy loss to find the most likely class.
+
+Ready to try your hand at image classification? Check out our complete [image classification guide](https://huggingface.co/docs/transformers/tasks/image_classification) to learn how to fine-tune ViT and use it for inference!
+
+
+> [!TIP]
+> Notice the parallel between ViT and BERT: both use a special token ([CLS]) to capture the overall representation, both add position information to their embeddings, and both use a Transformer encoder to process the sequence of tokens/patches.
diff --git a/chapters/en/chapter1/6.mdx b/chapters/en/chapter1/6.mdx
index 08e306e43..7b5e2dc5b 100644
--- a/chapters/en/chapter1/6.mdx
+++ b/chapters/en/chapter1/6.mdx
@@ -1,204 +1,204 @@
-
-
-# Transformer Architectures[[transformer-architectures]]
-
-In the previous sections, we introduced the general Transformer architecture and explored how these models can solve various tasks. Now, let's take a closer look at the three main architectural variants of Transformer models and understand when to use each one. Then, we look at how those architectures are applied to different language tasks.
-
-In this section, we're going to dive deeper into the three main architectural variants of Transformer models and understand when to use each one.
-
-
-> [!TIP]
-> Remember that most Transformer models use one of three architectures: encoder-only, decoder-only, or encoder-decoder (sequence-to-sequence). Understanding these differences will help you choose the right model for your specific task.
-
-## Encoder models[[encoder-models]]
-
-
-
-Encoder models use only the encoder of a Transformer model. At each stage, the attention layers can access all the words in the initial sentence. These models are often characterized as having "bi-directional" attention, and are often called *auto-encoding models*.
-
-The pretraining of these models usually revolves around somehow corrupting a given sentence (for instance, by masking random words in it) and tasking the model with finding or reconstructing the initial sentence.
-
-Encoder models are best suited for tasks requiring an understanding of the full sentence, such as sentence classification, named entity recognition (and more generally word classification), and extractive question answering.
-
-> [!TIP]
-> As we saw in [How 🤗 Transformers solve tasks](https://huggingface.co/learn/llm-course/chapter1/5), encoder models like BERT excel at understanding text because they can look at the entire context in both directions. This makes them perfect for tasks where comprehension of the whole input is important.
-
-Representatives of this family of models include:
-
-- [BERT](https://huggingface.co/docs/transformers/model_doc/bert)
-- [DistilBERT](https://huggingface.co/docs/transformers/model_doc/distilbert)
-- [ModernBERT](https://huggingface.co/docs/transformers/en/model_doc/modernbert)
-
-## Decoder models[[decoder-models]]
-
-
-
-Decoder models use only the decoder of a Transformer model. At each stage, for a given word the attention layers can only access the words positioned before it in the sentence. These models are often called *auto-regressive models*.
-
-The pretraining of decoder models usually revolves around predicting the next word in the sentence.
-
-These models are best suited for tasks involving text generation.
-
-> [!TIP]
-> Decoder models like GPT are designed to generate text by predicting one token at a time. As we explored in [How 🤗 Transformers solve tasks](https://huggingface.co/learn/llm-course/chapter1/5), they can only see previous tokens, which makes them excellent for creative text generation but less ideal for tasks requiring bidirectional understanding.
-
-Representatives of this family of models include:
-
-- [Hugging Face SmolLM Series](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct)
-- [Meta's Llama Series](https://huggingface.co/docs/transformers/en/model_doc/llama4)
-- [Google's Gemma Series](https://huggingface.co/docs/transformers/main/en/model_doc/gemma3)
-- [DeepSeek's V3](https://huggingface.co/deepseek-ai/DeepSeek-V3)
-
-### Modern Large Language Models (LLMs)
-
-Most modern Large Language Models (LLMs) use the decoder-only architecture. These models have grown dramatically in size and capabilities over the past few years, with some of the largest models containing hundreds of billions of parameters.
-
-Modern LLMs are typically trained in two phases:
-1. **Pretraining**: The model learns to predict the next token on vast amounts of text data
-2. **Instruction tuning**: The model is fine-tuned to follow instructions and generate helpful responses
-
-This approach has led to models that can understand and generate human-like text across a wide range of topics and tasks.
-
-#### Key capabilities of modern LLMs
-
-Modern decoder-based LLMs have demonstrated impressive capabilities:
-
-| Capability | Description | Example |
-|------------|-------------|---------|
-| Text generation | Creating coherent and contextually relevant text | Writing essays, stories, or emails |
-| Summarization | Condensing long documents into shorter versions | Creating executive summaries of reports |
-| Translation | Converting text between languages | Translating English to Spanish |
-| Question answering | Providing answers to factual questions | "What is the capital of France?" |
-| Code generation | Writing or completing code snippets | Creating a function based on a description |
-| Reasoning | Working through problems step by step | Solving math problems or logical puzzles |
-| Few-shot learning | Learning from a few examples in the prompt | Classifying text after seeing just 2-3 examples |
-
-You can experiment with decoder-based LLMs directly in your browser via model repo pages on the Hub. Here's an example with the classic [GPT-2](https://huggingface.co/openai-community/gpt2) (OpenAI's finest open source model!):
-
-
- View GPT-2 model on Hugging Face
-
-
-## Sequence-to-sequence models[[sequence-to-sequence-models]]
-
-
-
-Encoder-decoder models (also called *sequence-to-sequence models*) use both parts of the Transformer architecture. At each stage, the attention layers of the encoder can access all the words in the initial sentence, whereas the attention layers of the decoder can only access the words positioned before a given word in the input.
-
-The pretraining of these models can take different forms, but it often involves reconstructing a sentence for which the input has been somehow corrupted (for instance by masking random words). The pretraining of the T5 model consists of replacing random spans of text (that can contain several words) with a single mask special token, and the task is then to predict the text that this mask token replaces.
-
-Sequence-to-sequence models are best suited for tasks revolving around generating new sentences depending on a given input, such as summarization, translation, or generative question answering.
-
-> [!TIP]
-> As we saw in [How 🤗 Transformers solve tasks](https://huggingface.co/learn/llm-course/chapter1/5), encoder-decoder models like BART and T5 combine the strengths of both architectures. The encoder provides deep bidirectional understanding of the input, while the decoder generates appropriate output text. This makes them perfect for tasks that transform one sequence into another, like translation or summarization.
-
-### Practical applications
-
-Sequence-to-sequence models excel at tasks that require transforming one form of text into another while preserving meaning. Some practical applications include:
-
-| Application | Description | Example Model |
-|-------------|-------------|---------------|
-| Machine translation | Converting text between languages | Marian, T5 |
-| Text summarization | Creating concise summaries of longer texts | BART, T5 |
-| Data-to-text generation | Converting structured data into natural language | T5 |
-| Grammar correction | Fixing grammatical errors in text | T5 |
-| Question answering | Generating answers based on context | BART, T5 |
-
-Here's an interactive demo of a sequence-to-sequence model for translation:
-
-
-
-Representatives of this family of models include:
-
-- [BART](https://huggingface.co/docs/transformers/model_doc/bart)
-- [mBART](https://huggingface.co/docs/transformers/model_doc/mbart)
-- [Marian](https://huggingface.co/docs/transformers/model_doc/marian)
-- [T5](https://huggingface.co/docs/transformers/model_doc/t5)
-
-## Choosing the right architecture[[choosing-the-right-architecture]]
-
-When working on a specific NLP task, how do you decide which architecture to use? Here's a quick guide:
-
-| Task | Suggested Architecture | Examples |
-|------|------------------------|----------|
-| Text classification (sentiment, topic) | Encoder | BERT, RoBERTa |
-| Text generation (creative writing) | Decoder | GPT, LLaMA |
-| Translation | Encoder-Decoder | T5, BART |
-| Summarization | Encoder-Decoder | BART, T5 |
-| Named entity recognition | Encoder | BERT, RoBERTa |
-| Question answering (extractive) | Encoder | BERT, RoBERTa |
-| Question answering (generative) | Encoder-Decoder or Decoder | T5, GPT |
-| Conversational AI | Decoder | GPT, LLaMA |
-
-> [!TIP]
-> When in doubt about which model to use, consider:
->
-> 1. What kind of understanding does your task need? (Bidirectional or unidirectional)
-> 2. Are you generating new text or analyzing existing text?
-> 3. Do you need to transform one sequence into another?
->
-> The answers to these questions will guide you toward the right architecture.
-
-## The evolution of LLMs
-
-Large Language Models have evolved rapidly in recent years, with each generation bringing significant improvements in capabilities.
-
-## Attention mechanisms[[attention-mechanisms]]
-
-Most transformer models use full attention in the sense that the attention matrix is square. It can be a big
-computational bottleneck when you have long texts. Longformer and reformer are models that try to be more efficient and
-use a sparse version of the attention matrix to speed up training.
-
-> [!TIP]
-> Standard attention mechanisms have a computational complexity of O(n²), where n is the sequence length. This becomes problematic for very long sequences. The specialized attention mechanisms below help address this limitation.
-
-### LSH attention
-
-[Reformer](https://huggingface.co/docs/transformers/model_doc/reformer) uses LSH attention. In the softmax(QK^t), only the biggest elements (in the softmax dimension) of the matrix QK^t are going to give useful contributions. So for each query q in Q, we can consider only
-the keys k in K that are close to q. A hash function is used to determine if q and k are close. The attention mask is
-modified to mask the current token (except at the first position), because it will give a query and a key equal (so
-very similar to each other). Since the hash can be a bit random, several hash functions are used in practice
-(determined by a n_rounds parameter) and then are averaged together.
-
-### Local attention
-
-[Longformer](https://huggingface.co/docs/transformers/model_doc/longformer) uses local attention: often, the local context (e.g., what are the two tokens to the left and right?) is enough to take action for a given token. Also, by stacking attention layers that have a small
-window, the last layer will have a receptive field of more than just the tokens in the window, allowing them to build a
-representation of the whole sentence.
-
-Some preselected input tokens are also given global attention: for those few tokens, the attention matrix can access
-all tokens and this process is symmetric: all other tokens have access to those specific tokens (on top of the ones in
-their local window). This is shown in Figure 2d of the paper, see below for a sample attention mask:
-
-
-

-
-
-Using those attention matrices with less parameters then allows the model to have inputs having a bigger sequence
-length.
-
-### Axial positional encodings
-
-[Reformer](https://huggingface.co/docs/transformers/model_doc/reformer) uses axial positional encodings: in traditional transformer models, the positional encoding
-E is a matrix of size \\(l\\) by \\(d\\), \\(l\\) being the sequence length and \\(d\\) the dimension of the
-hidden state. If you have very long texts, this matrix can be huge and take way too much space on the GPU. To alleviate
-that, axial positional encodings consist of factorizing that big matrix E in two smaller matrices E1 and E2, with
-dimensions \\(l_{1} \times d_{1}\\) and \\(l_{2} \times d_{2}\\), such that \\(l_{1} \times l_{2} = l\\) and
-\\(d_{1} + d_{2} = d\\) (with the product for the lengths, this ends up being way smaller). The embedding for time
-step \\(j\\) in E is obtained by concatenating the embeddings for timestep \\(j \% l1\\) in E1 and \\(j // l1\\)
-in E2.
-
-## Conclusion[[conclusion]]
-
-In this section, we've explored the three main Transformer architectures and some specialized attention mechanisms. Understanding these architectural differences is crucial for selecting the right model for your specific NLP task.
-
-As we move forward in the course, you'll get hands-on experience with these different architectures and learn how to fine-tune them for your specific needs. In the next section, we'll look at some of the limitations and biases present in these models that you should be aware of when deploying them.
+
+
+# Transformer Architectures[[transformer-architectures]]
+
+In the previous sections, we introduced the general Transformer architecture and explored how these models can solve various tasks. Now, let's take a closer look at the three main architectural variants of Transformer models and understand when to use each one. Then, we look at how those architectures are applied to different language tasks.
+
+In this section, we're going to dive deeper into the three main architectural variants of Transformer models and understand when to use each one.
+
+
+> [!TIP]
+> Remember that most Transformer models use one of three architectures: encoder-only, decoder-only, or encoder-decoder (sequence-to-sequence). Understanding these differences will help you choose the right model for your specific task.
+
+## Encoder models[[encoder-models]]
+
+
+
+Encoder models use only the encoder of a Transformer model. At each stage, the attention layers can access all the words in the initial sentence. These models are often characterized as having "bi-directional" attention, and are often called *auto-encoding models*.
+
+The pretraining of these models usually revolves around somehow corrupting a given sentence (for instance, by masking random words in it) and tasking the model with finding or reconstructing the initial sentence.
+
+Encoder models are best suited for tasks requiring an understanding of the full sentence, such as sentence classification, named entity recognition (and more generally word classification), and extractive question answering.
+
+> [!TIP]
+> As we saw in [How 🤗 Transformers solve tasks](https://huggingface.co/learn/llm-course/chapter1/5), encoder models like BERT excel at understanding text because they can look at the entire context in both directions. This makes them perfect for tasks where comprehension of the whole input is important.
+
+Representatives of this family of models include:
+
+- [BERT](https://huggingface.co/docs/transformers/model_doc/bert)
+- [DistilBERT](https://huggingface.co/docs/transformers/model_doc/distilbert)
+- [ModernBERT](https://huggingface.co/docs/transformers/en/model_doc/modernbert)
+
+## Decoder models[[decoder-models]]
+
+
+
+Decoder models use only the decoder of a Transformer model. At each stage, for a given word the attention layers can only access the words positioned before it in the sentence. These models are often called *auto-regressive models*.
+
+The pretraining of decoder models usually revolves around predicting the next word in the sentence.
+
+These models are best suited for tasks involving text generation.
+
+> [!TIP]
+> Decoder models like GPT are designed to generate text by predicting one token at a time. As we explored in [How 🤗 Transformers solve tasks](https://huggingface.co/learn/llm-course/chapter1/5), they can only see previous tokens, which makes them excellent for creative text generation but less ideal for tasks requiring bidirectional understanding.
+
+Representatives of this family of models include:
+
+- [Hugging Face SmolLM Series](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct)
+- [Meta's Llama Series](https://huggingface.co/docs/transformers/en/model_doc/llama4)
+- [Google's Gemma Series](https://huggingface.co/docs/transformers/main/en/model_doc/gemma3)
+- [DeepSeek's V3](https://huggingface.co/deepseek-ai/DeepSeek-V3)
+
+### Modern Large Language Models (LLMs)
+
+Most modern Large Language Models (LLMs) use the decoder-only architecture. These models have grown dramatically in size and capabilities over the past few years, with some of the largest models containing hundreds of billions of parameters.
+
+Modern LLMs are typically trained in two phases:
+1. **Pretraining**: The model learns to predict the next token on vast amounts of text data
+2. **Instruction tuning**: The model is fine-tuned to follow instructions and generate helpful responses
+
+This approach has led to models that can understand and generate human-like text across a wide range of topics and tasks.
+
+#### Key capabilities of modern LLMs
+
+Modern decoder-based LLMs have demonstrated impressive capabilities:
+
+| Capability | Description | Example |
+|------------|-------------|---------|
+| Text generation | Creating coherent and contextually relevant text | Writing essays, stories, or emails |
+| Summarization | Condensing long documents into shorter versions | Creating executive summaries of reports |
+| Translation | Converting text between languages | Translating English to Spanish |
+| Question answering | Providing answers to factual questions | "What is the capital of France?" |
+| Code generation | Writing or completing code snippets | Creating a function based on a description |
+| Reasoning | Working through problems step by step | Solving math problems or logical puzzles |
+| Few-shot learning | Learning from a few examples in the prompt | Classifying text after seeing just 2-3 examples |
+
+You can experiment with decoder-based LLMs directly in your browser via model repo pages on the Hub. Here's an example with the classic [GPT-2](https://huggingface.co/openai-community/gpt2) (OpenAI's finest open source model!):
+
+
+ View GPT-2 model on Hugging Face
+
+
+## Sequence-to-sequence models[[sequence-to-sequence-models]]
+
+
+
+Encoder-decoder models (also called *sequence-to-sequence models*) use both parts of the Transformer architecture. At each stage, the attention layers of the encoder can access all the words in the initial sentence, whereas the attention layers of the decoder can only access the words positioned before a given word in the input.
+
+The pretraining of these models can take different forms, but it often involves reconstructing a sentence for which the input has been somehow corrupted (for instance by masking random words). The pretraining of the T5 model consists of replacing random spans of text (that can contain several words) with a single mask special token, and the task is then to predict the text that this mask token replaces.
+
+Sequence-to-sequence models are best suited for tasks revolving around generating new sentences depending on a given input, such as summarization, translation, or generative question answering.
+
+> [!TIP]
+> As we saw in [How 🤗 Transformers solve tasks](https://huggingface.co/learn/llm-course/chapter1/5), encoder-decoder models like BART and T5 combine the strengths of both architectures. The encoder provides deep bidirectional understanding of the input, while the decoder generates appropriate output text. This makes them perfect for tasks that transform one sequence into another, like translation or summarization.
+
+### Practical applications
+
+Sequence-to-sequence models excel at tasks that require transforming one form of text into another while preserving meaning. Some practical applications include:
+
+| Application | Description | Example Model |
+|-------------|-------------|---------------|
+| Machine translation | Converting text between languages | Marian, T5 |
+| Text summarization | Creating concise summaries of longer texts | BART, T5 |
+| Data-to-text generation | Converting structured data into natural language | T5 |
+| Grammar correction | Fixing grammatical errors in text | T5 |
+| Question answering | Generating answers based on context | BART, T5 |
+
+Here's an interactive demo of a sequence-to-sequence model for translation:
+
+
+
+Representatives of this family of models include:
+
+- [BART](https://huggingface.co/docs/transformers/model_doc/bart)
+- [mBART](https://huggingface.co/docs/transformers/model_doc/mbart)
+- [Marian](https://huggingface.co/docs/transformers/model_doc/marian)
+- [T5](https://huggingface.co/docs/transformers/model_doc/t5)
+
+## Choosing the right architecture[[choosing-the-right-architecture]]
+
+When working on a specific NLP task, how do you decide which architecture to use? Here's a quick guide:
+
+| Task | Suggested Architecture | Examples |
+|------|------------------------|----------|
+| Text classification (sentiment, topic) | Encoder | BERT, RoBERTa |
+| Text generation (creative writing) | Decoder | GPT, LLaMA |
+| Translation | Encoder-Decoder | T5, BART |
+| Summarization | Encoder-Decoder | BART, T5 |
+| Named entity recognition | Encoder | BERT, RoBERTa |
+| Question answering (extractive) | Encoder | BERT, RoBERTa |
+| Question answering (generative) | Encoder-Decoder or Decoder | T5, GPT |
+| Conversational AI | Decoder | GPT, LLaMA |
+
+> [!TIP]
+> When in doubt about which model to use, consider:
+>
+> 1. What kind of understanding does your task need? (Bidirectional or unidirectional)
+> 2. Are you generating new text or analyzing existing text?
+> 3. Do you need to transform one sequence into another?
+>
+> The answers to these questions will guide you toward the right architecture.
+
+## The evolution of LLMs
+
+Large Language Models have evolved rapidly in recent years, with each generation bringing significant improvements in capabilities.
+
+## Attention mechanisms[[attention-mechanisms]]
+
+Most transformer models use full attention in the sense that the attention matrix is square. It can be a big
+computational bottleneck when you have long texts. Longformer and reformer are models that try to be more efficient and
+use a sparse version of the attention matrix to speed up training.
+
+> [!TIP]
+> Standard attention mechanisms have a computational complexity of O(n²), where n is the sequence length. This becomes problematic for very long sequences. The specialized attention mechanisms below help address this limitation.
+
+### LSH attention
+
+[Reformer](https://huggingface.co/docs/transformers/model_doc/reformer) uses LSH attention. In the softmax(QK^t), only the biggest elements (in the softmax dimension) of the matrix QK^t are going to give useful contributions. So for each query q in Q, we can consider only
+the keys k in K that are close to q. A hash function is used to determine if q and k are close. The attention mask is
+modified to mask the current token (except at the first position), because it will give a query and a key equal (so
+very similar to each other). Since the hash can be a bit random, several hash functions are used in practice
+(determined by a n_rounds parameter) and then are averaged together.
+
+### Local attention
+
+[Longformer](https://huggingface.co/docs/transformers/model_doc/longformer) uses local attention: often, the local context (e.g., what are the two tokens to the left and right?) is enough to take action for a given token. Also, by stacking attention layers that have a small
+window, the last layer will have a receptive field of more than just the tokens in the window, allowing them to build a
+representation of the whole sentence.
+
+Some preselected input tokens are also given global attention: for those few tokens, the attention matrix can access
+all tokens and this process is symmetric: all other tokens have access to those specific tokens (on top of the ones in
+their local window). This is shown in Figure 2d of the paper, see below for a sample attention mask:
+
+
+

+
+
+Using those attention matrices with less parameters then allows the model to have inputs having a bigger sequence
+length.
+
+### Axial positional encodings
+
+[Reformer](https://huggingface.co/docs/transformers/model_doc/reformer) uses axial positional encodings: in traditional transformer models, the positional encoding
+E is a matrix of size \\(l\\) by \\(d\\), \\(l\\) being the sequence length and \\(d\\) the dimension of the
+hidden state. If you have very long texts, this matrix can be huge and take way too much space on the GPU. To alleviate
+that, axial positional encodings consist of factorizing that big matrix E in two smaller matrices E1 and E2, with
+dimensions \\(l_{1} \times d_{1}\\) and \\(l_{2} \times d_{2}\\), such that \\(l_{1} \times l_{2} = l\\) and
+\\(d_{1} + d_{2} = d\\) (with the product for the lengths, this ends up being way smaller). The embedding for time
+step \\(j\\) in E is obtained by concatenating the embeddings for timestep \\(j \% l1\\) in E1 and \\(j // l1\\)
+in E2.
+
+## Conclusion[[conclusion]]
+
+In this section, we've explored the three main Transformer architectures and some specialized attention mechanisms. Understanding these architectural differences is crucial for selecting the right model for your specific NLP task.
+
+As we move forward in the course, you'll get hands-on experience with these different architectures and learn how to fine-tune them for your specific needs. In the next section, we'll look at some of the limitations and biases present in these models that you should be aware of when deploying them.
diff --git a/chapters/en/chapter1/7.mdx b/chapters/en/chapter1/7.mdx
index ae770eaca..475e54412 100644
--- a/chapters/en/chapter1/7.mdx
+++ b/chapters/en/chapter1/7.mdx
@@ -1,257 +1,257 @@
-
-
-# Ungraded quiz[[ungraded-quiz]]
-
-
-
-So far, this chapter has covered a lot of ground! Don't worry if you didn't grasp all the details, but it's to reflect on what you've learned so far with a quiz.
-
-This quiz is ungraded, so you can try it as many times as you want. If you struggle with some questions, follow the tips and revisit the material. You'll be quizzed on this material again in the certification exam.
-
-### 1. Explore the Hub and look for the `roberta-large-mnli` checkpoint. What task does it perform?
-
-
-roberta-large-mnli page."
- },
- {
- text: "Text classification",
- explain: "More precisely, it classifies if two sentences are logically linked across three labels (contradiction, neutral, entailment) — a task also called natural language inference.",
- correct: true
- },
- {
- text: "Text generation",
- explain: "Look again on the roberta-large-mnli page."
- }
- ]}
-/>
-
-### 2. What will the following code return?
-
-```py
-from transformers import pipeline
-
-ner = pipeline("ner", aggregation_strategy="simple")
-ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
-```
-
-sentiment-analysis pipeline."
- },
- {
- text: "It will return a generated text completing this sentence.",
- explain: "This is incorrect — it would be a text-generation pipeline.",
- },
- {
- text: "It will return the words representing persons, organizations or locations.",
- explain: "Furthermore, with aggregation_strategy="simple", it will group together the words belonging to the same entity, like \"Hugging Face\".",
- correct: true
- }
- ]}
-/>
-
-### 3. What should replace ... in this code sample?
-
-```py
-from transformers import pipeline
-
-filler = pipeline("fill-mask", model="bert-base-cased")
-result = filler("...")
-```
-
- has been waiting for you.",
- explain: "This is incorrect. Check out the bert-base-cased model card and try to spot your mistake."
- },
- {
- text: "This [MASK] has been waiting for you.",
- explain: "This model's mask token is [MASK].",
- correct: true
- },
- {
- text: "This man has been waiting for you.",
- explain: "This is incorrect. This pipeline fills in masked words, so it needs a mask token somewhere."
- }
- ]}
-/>
-
-### 4. Why will this code fail?
-
-```py
-from transformers import pipeline
-
-classifier = pipeline("zero-shot-classification")
-result = classifier("This is a course about the Transformers library")
-```
-
-candidate_labels=[...].",
- correct: true
- },
- {
- text: "This pipeline requires several sentences, not just one.",
- explain: "This is incorrect, though when properly used, this pipeline can take a list of sentences to process (like all other pipelines)."
- },
- {
- text: "The 🤗 Transformers library is broken, as usual.",
- explain: "We won't dignify this answer with a comment!"
- },
- {
- text: "This pipeline requires longer inputs; this one is too short.",
- explain: "This is incorrect. Note that a very long text will be truncated when processed by this pipeline."
- }
- ]}
-/>
-
-### 5. What does "transfer learning" mean?
-
-
-
-### 6. True or false? A language model usually does not need labels for its pretraining.
-
-self-supervised, which means the labels are created automatically from the inputs (like predicting the next word or filling in some masked words).",
- correct: true
- },
- {
- text: "False",
- explain: "This is not the correct answer."
- }
- ]}
-/>
-
-### 7. Select the sentence that best describes the terms "model", "architecture", and "weights".
-
-
-
-
-### 8. Which of these types of models would you use for completing prompts with generated text?
-
-
-
-### 9. Which of those types of models would you use for summarizing texts?
-
-
-
-### 10. Which of these types of models would you use for classifying text inputs according to certain labels?
-
-
-
-### 11. What possible source can the bias observed in a model have?
-
-
+
+
+# Ungraded quiz[[ungraded-quiz]]
+
+
+
+So far, this chapter has covered a lot of ground! Don't worry if you didn't grasp all the details, but it's to reflect on what you've learned so far with a quiz.
+
+This quiz is ungraded, so you can try it as many times as you want. If you struggle with some questions, follow the tips and revisit the material. You'll be quizzed on this material again in the certification exam.
+
+### 1. Explore the Hub and look for the `roberta-large-mnli` checkpoint. What task does it perform?
+
+
+roberta-large-mnli page."
+ },
+ {
+ text: "Text classification",
+ explain: "More precisely, it classifies if two sentences are logically linked across three labels (contradiction, neutral, entailment) — a task also called natural language inference.",
+ correct: true
+ },
+ {
+ text: "Text generation",
+ explain: "Look again on the roberta-large-mnli page."
+ }
+ ]}
+/>
+
+### 2. What will the following code return?
+
+```py
+from transformers import pipeline
+
+ner = pipeline("ner", aggregation_strategy="simple")
+ner("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+sentiment-analysis pipeline."
+ },
+ {
+ text: "It will return a generated text completing this sentence.",
+ explain: "This is incorrect — it would be a text-generation pipeline.",
+ },
+ {
+ text: "It will return the words representing persons, organizations or locations.",
+ explain: "Furthermore, with aggregation_strategy="simple", it will group together the words belonging to the same entity, like \"Hugging Face\".",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. What should replace ... in this code sample?
+
+```py
+from transformers import pipeline
+
+filler = pipeline("fill-mask", model="bert-base-cased")
+result = filler("...")
+```
+
+ has been waiting for you.",
+ explain: "This is incorrect. Check out the bert-base-cased model card and try to spot your mistake."
+ },
+ {
+ text: "This [MASK] has been waiting for you.",
+ explain: "This model's mask token is [MASK].",
+ correct: true
+ },
+ {
+ text: "This man has been waiting for you.",
+ explain: "This is incorrect. This pipeline fills in masked words, so it needs a mask token somewhere."
+ }
+ ]}
+/>
+
+### 4. Why will this code fail?
+
+```py
+from transformers import pipeline
+
+classifier = pipeline("zero-shot-classification")
+result = classifier("This is a course about the Transformers library")
+```
+
+candidate_labels=[...].",
+ correct: true
+ },
+ {
+ text: "This pipeline requires several sentences, not just one.",
+ explain: "This is incorrect, though when properly used, this pipeline can take a list of sentences to process (like all other pipelines)."
+ },
+ {
+ text: "The 🤗 Transformers library is broken, as usual.",
+ explain: "We won't dignify this answer with a comment!"
+ },
+ {
+ text: "This pipeline requires longer inputs; this one is too short.",
+ explain: "This is incorrect. Note that a very long text will be truncated when processed by this pipeline."
+ }
+ ]}
+/>
+
+### 5. What does "transfer learning" mean?
+
+
+
+### 6. True or false? A language model usually does not need labels for its pretraining.
+
+self-supervised, which means the labels are created automatically from the inputs (like predicting the next word or filling in some masked words).",
+ correct: true
+ },
+ {
+ text: "False",
+ explain: "This is not the correct answer."
+ }
+ ]}
+/>
+
+### 7. Select the sentence that best describes the terms "model", "architecture", and "weights".
+
+
+
+
+### 8. Which of these types of models would you use for completing prompts with generated text?
+
+
+
+### 9. Which of those types of models would you use for summarizing texts?
+
+
+
+### 10. Which of these types of models would you use for classifying text inputs according to certain labels?
+
+
+
+### 11. What possible source can the bias observed in a model have?
+
+
diff --git a/chapters/en/chapter1/8.mdx b/chapters/en/chapter1/8.mdx
index 6be1cb515..de3c0ef1b 100644
--- a/chapters/en/chapter1/8.mdx
+++ b/chapters/en/chapter1/8.mdx
@@ -1,263 +1,263 @@
-# Deep dive into Text Generation Inference with LLMs[[inference-with-llms]]
-
-
-
-
-
-So far, we've explored the transformer architecture in relation to a range of discrete tasks, like text classification or summarization. However, Large Language Models are most used for text generation, and this is what we'll explore in this chapter.
-
-In this page, we'll explore the core concepts behind LLM inference, providing a comprehensive understanding of how these models generate text and the key components involved in the inference process.
-
-## Understanding the Basics
-
-Let's start with the fundamentals. Inference is the process of using a trained LLM to generate human-like text from a given input prompt. Language models use their knowledge from training to formulate responses one word at a time. The model leverages learned probabilities from billions of parameters to predict and generate the next token in a sequence. This sequential generation is what allows LLMs to produce coherent and contextually relevant text.
-
-## The Role of Attention
-
-The attention mechanism is what gives LLMs their ability to understand context and generate coherent responses. When predicting the next word, not every word in a sentence carries equal weight - for example, in the sentence *"The capital of France is ..."*, the words "France" and "capital" are crucial for determining that "Paris" should come next. This ability to focus on relevant information is what we call attention.
-
-
-
-This process of identifying the most relevant words to predict the next token has proven to be incredibly effective. Although the basic principle of training LLMs—predicting the next token—has remained generally consistent since BERT and GPT-2, there have been significant advancements in scaling neural networks and making the attention mechanism work for longer and longer sequences, at lower and lower costs.
-
-> [!TIP]
-> In short, the attention mechanism is the key to LLMs being able to generate text that is both coherent and context-aware. It sets modern LLMs apart from previous generations of language models.
-
-### Context Length and Attention Span
-
-Now that we understand attention, let's explore how much context an LLM can actually handle. This brings us to context length, or the model's 'attention span'.
-
-The context length refers to the maximum number of tokens (words or parts of words) that the LLM can process at once. Think of it as the size of the model's working memory.
-
-These capabilities are limited by several practical factors:
-- The model's architecture and size
-- Available computational resources
-- The complexity of the input and desired output
-
-In an ideal world, we could feed unlimited context to the model, but hardware constraints and computational costs make this impractical. This is why different models are designed with different context lengths to balance capability with efficiency.
-
-> [!TIP]
-> The context length is the maximum number of tokens the model can consider at once when generating a response.
-
-### The Art of Prompting
-
-When we pass information to LLMs, we structure our input in a way that guides the generation of the LLM toward the desired output. This is called _prompting_.
-
-Understanding how LLMs process information helps us craft better prompts. Since the model's primary task is to predict the next token by analyzing the importance of each input token, the wording of your input sequence becomes crucial.
-
-> [!TIP]
-> Careful design of the prompt makes it easier **to guide the generation of the LLM toward the desired output**.
-
-## The Two-Phase Inference Process
-
-Now that we understand the basic components, let's dive into how LLMs actually generate text. The process can be broken down into two main phases: prefill and decode. These phases work together like an assembly line, each playing a crucial role in producing coherent text.
-
-### The Prefill Phase
-
-The prefill phase is like the preparation stage in cooking - it's where all the initial ingredients are processed and made ready. This phase involves three key steps:
-
-1. **Tokenization**: Converting the input text into tokens (think of these as the basic building blocks the model understands)
-2. **Embedding Conversion**: Transforming these tokens into numerical representations that capture their meaning
-3. **Initial Processing**: Running these embeddings through the model's neural networks to create a rich understanding of the context
-
-This phase is computationally intensive because it needs to process all input tokens at once. Think of it as reading and understanding an entire paragraph before starting to write a response.
-
-You can experiment with different tokenizers in the interactive playground below:
-
-
-
-### The Decode Phase
-
-After the prefill phase has processed the input, we move to the decode phase - this is where the actual text generation happens. The model generates one token at a time in what we call an autoregressive process (where each new token depends on all previous tokens).
-
-The decode phase involves several key steps that happen for each new token:
-1. **Attention Computation**: Looking back at all previous tokens to understand context
-2. **Probability Calculation**: Determining the likelihood of each possible next token
-3. **Token Selection**: Choosing the next token based on these probabilities
-4. **Continuation Check**: Deciding whether to continue or stop generation
-
-This phase is memory-intensive because the model needs to keep track of all previously generated tokens and their relationships.
-
-## Sampling Strategies
-
-Now that we understand how the model generates text, let's explore the various ways we can control this generation process. Just like a writer might choose between being more creative or more precise, we can adjust how the model makes its token selections.
-
-You can interact with the basic decoding process yourself with SmolLM2 in this Space (remember, it decodes until reaching an **EOS** token which is **<|im_end|>** for this model):
-
-
-
-### Understanding Token Selection: From Probabilities to Token Choices
-
-When the model needs to choose the next token, it starts with raw probabilities (called logits) for every word in its vocabulary. But how do we turn these probabilities into actual choices? Let's break down the process:
-
-
-
-1. **Raw Logits**: Think of these as the model's initial gut feelings about each possible next word
-2. **Temperature Control**: Like a creativity dial - higher settings (>1.0) make choices more random and creative, lower settings (<1.0) make them more focused and deterministic
-3. **Top-p (Nucleus) Sampling**: Instead of considering all possible words, we only look at the most likely ones that add up to our chosen probability threshold (e.g., top 90%)
-4. **Top-k Filtering**: An alternative approach where we only consider the k most likely next words
-
-### Managing Repetition: Keeping Output Fresh
-
-One common challenge with LLMs is their tendency to repeat themselves - much like a speaker who keeps returning to the same points. To address this, we use two types of penalties:
-
-1. **Presence Penalty**: A fixed penalty applied to any token that has appeared before, regardless of how often. This helps prevent the model from reusing the same words.
-2. **Frequency Penalty**: A scaling penalty that increases based on how often a token has been used. The more a word appears, the less likely it is to be chosen again.
-
-
-
-These penalties are applied early in the token selection process, adjusting the raw probabilities before other sampling strategies are applied. Think of them as gentle nudges encouraging the model to explore new vocabulary.
-
-### Controlling Generation Length: Setting Boundaries
-
-Just as a good story needs proper pacing and length, we need ways to control how much text our LLM generates. This is crucial for practical applications - whether we're generating a tweet-length response or a full blog post.
-
-We can control generation length in several ways:
-1. **Token Limits**: Setting minimum and maximum token counts
-2. **Stop Sequences**: Defining specific patterns that signal the end of generation
-3. **End-of-Sequence Detection**: Letting the model naturally conclude its response
-
-For example, if we want to generate a single paragraph, we might set a maximum of 100 tokens and use "\n\n" as a stop sequence. This ensures our output stays focused and appropriately sized for its purpose.
-
-
-
-### Beam Search: Looking Ahead for Better Coherence
-
-While the strategies we've discussed so far make decisions one token at a time, beam search takes a more holistic approach. Instead of committing to a single choice at each step, it explores multiple possible paths simultaneously - like a chess player thinking several moves ahead.
-
-
-
-Here's how it works:
-1. At each step, maintain multiple candidate sequences (typically 5-10)
-2. For each candidate, compute probabilities for the next token
-3. Keep only the most promising combinations of sequences and next tokens
-4. Continue this process until reaching the desired length or stop condition
-5. Select the sequence with the highest overall probability
-
-You can explore beam search visually here:
-
-
-
-This approach often produces more coherent and grammatically correct text, though it requires more computational resources than simpler methods.
-
-## Practical Challenges and Optimization
-
-As we wrap up our exploration of LLM inference, let's look at the practical challenges you'll face when deploying these models, and how to measure and optimize their performance.
-
-### Key Performance Metrics
-
-When working with LLMs, four critical metrics will shape your implementation decisions:
-
-1. **Time to First Token (TTFT)**: How quickly can you get the first response? This is crucial for user experience and is primarily affected by the prefill phase.
-2. **Time Per Output Token (TPOT)**: How fast can you generate subsequent tokens? This determines the overall generation speed.
-3. **Throughput**: How many requests can you handle simultaneously? This affects scaling and cost efficiency.
-4. **VRAM Usage**: How much GPU memory do you need? This often becomes the primary constraint in real-world applications.
-
-### The Context Length Challenge
-
-One of the most significant challenges in LLM inference is managing context length effectively. Longer contexts provide more information but come with substantial costs:
-
-- **Memory Usage**: Grows quadratically with context length
-- **Processing Speed**: Decreases linearly with longer contexts
-- **Resource Allocation**: Requires careful balancing of VRAM usage
-
-Recent models like [Qwen2.5-1M](https://huggingface.co/Qwen/Qwen2.5-14B-Instruct-1M) offer impressive 1M token context windows, but this comes at the cost of significantly slower inference times. The key is finding the right balance for your specific use case.
-
-
-
-
-
-
- Input Text (Raw)
-
-
→
-
- Tokenized Input
-
-
-
-
- Context Window
(e.g., 4K tokens)
-
-
-
-
-
-
- Memory Usage
∝ Length²
-
-
-
-
- Processing Time
∝ Length
-
-
-
-
-
-
-### The KV Cache Optimization
-
-To address these challenges, one of the most powerful optimizations is KV (Key-Value) caching. This technique significantly improves inference speed by storing and reusing intermediate calculations. This optimization:
-- Reduces repeated calculations
-- Improves generation speed
-- Makes long-context generation practical
-
-The trade-off is additional memory usage, but the performance benefits usually far outweigh this cost.
-
-## Conclusion
-
-Understanding LLM inference is crucial for effectively deploying and optimizing these powerful models. We've covered the key components:
-
-- The fundamental role of attention and context
-- The two-phase inference process
-- Various sampling strategies for controlling generation
-- Practical challenges and optimizations
-
-By mastering these concepts, you'll be better equipped to build applications that leverage LLMs effectively and efficiently.
-
-Remember that the field of LLM inference is rapidly evolving, with new techniques and optimizations emerging regularly. Stay curious and keep experimenting with different approaches to find what works best for your specific use cases.
+# Deep dive into Text Generation Inference with LLMs[[inference-with-llms]]
+
+
+
+
+
+So far, we've explored the transformer architecture in relation to a range of discrete tasks, like text classification or summarization. However, Large Language Models are most used for text generation, and this is what we'll explore in this chapter.
+
+In this page, we'll explore the core concepts behind LLM inference, providing a comprehensive understanding of how these models generate text and the key components involved in the inference process.
+
+## Understanding the Basics
+
+Let's start with the fundamentals. Inference is the process of using a trained LLM to generate human-like text from a given input prompt. Language models use their knowledge from training to formulate responses one word at a time. The model leverages learned probabilities from billions of parameters to predict and generate the next token in a sequence. This sequential generation is what allows LLMs to produce coherent and contextually relevant text.
+
+## The Role of Attention
+
+The attention mechanism is what gives LLMs their ability to understand context and generate coherent responses. When predicting the next word, not every word in a sentence carries equal weight - for example, in the sentence *"The capital of France is ..."*, the words "France" and "capital" are crucial for determining that "Paris" should come next. This ability to focus on relevant information is what we call attention.
+
+
+
+This process of identifying the most relevant words to predict the next token has proven to be incredibly effective. Although the basic principle of training LLMs—predicting the next token—has remained generally consistent since BERT and GPT-2, there have been significant advancements in scaling neural networks and making the attention mechanism work for longer and longer sequences, at lower and lower costs.
+
+> [!TIP]
+> In short, the attention mechanism is the key to LLMs being able to generate text that is both coherent and context-aware. It sets modern LLMs apart from previous generations of language models.
+
+### Context Length and Attention Span
+
+Now that we understand attention, let's explore how much context an LLM can actually handle. This brings us to context length, or the model's 'attention span'.
+
+The context length refers to the maximum number of tokens (words or parts of words) that the LLM can process at once. Think of it as the size of the model's working memory.
+
+These capabilities are limited by several practical factors:
+- The model's architecture and size
+- Available computational resources
+- The complexity of the input and desired output
+
+In an ideal world, we could feed unlimited context to the model, but hardware constraints and computational costs make this impractical. This is why different models are designed with different context lengths to balance capability with efficiency.
+
+> [!TIP]
+> The context length is the maximum number of tokens the model can consider at once when generating a response.
+
+### The Art of Prompting
+
+When we pass information to LLMs, we structure our input in a way that guides the generation of the LLM toward the desired output. This is called _prompting_.
+
+Understanding how LLMs process information helps us craft better prompts. Since the model's primary task is to predict the next token by analyzing the importance of each input token, the wording of your input sequence becomes crucial.
+
+> [!TIP]
+> Careful design of the prompt makes it easier **to guide the generation of the LLM toward the desired output**.
+
+## The Two-Phase Inference Process
+
+Now that we understand the basic components, let's dive into how LLMs actually generate text. The process can be broken down into two main phases: prefill and decode. These phases work together like an assembly line, each playing a crucial role in producing coherent text.
+
+### The Prefill Phase
+
+The prefill phase is like the preparation stage in cooking - it's where all the initial ingredients are processed and made ready. This phase involves three key steps:
+
+1. **Tokenization**: Converting the input text into tokens (think of these as the basic building blocks the model understands)
+2. **Embedding Conversion**: Transforming these tokens into numerical representations that capture their meaning
+3. **Initial Processing**: Running these embeddings through the model's neural networks to create a rich understanding of the context
+
+This phase is computationally intensive because it needs to process all input tokens at once. Think of it as reading and understanding an entire paragraph before starting to write a response.
+
+You can experiment with different tokenizers in the interactive playground below:
+
+
+
+### The Decode Phase
+
+After the prefill phase has processed the input, we move to the decode phase - this is where the actual text generation happens. The model generates one token at a time in what we call an autoregressive process (where each new token depends on all previous tokens).
+
+The decode phase involves several key steps that happen for each new token:
+1. **Attention Computation**: Looking back at all previous tokens to understand context
+2. **Probability Calculation**: Determining the likelihood of each possible next token
+3. **Token Selection**: Choosing the next token based on these probabilities
+4. **Continuation Check**: Deciding whether to continue or stop generation
+
+This phase is memory-intensive because the model needs to keep track of all previously generated tokens and their relationships.
+
+## Sampling Strategies
+
+Now that we understand how the model generates text, let's explore the various ways we can control this generation process. Just like a writer might choose between being more creative or more precise, we can adjust how the model makes its token selections.
+
+You can interact with the basic decoding process yourself with SmolLM2 in this Space (remember, it decodes until reaching an **EOS** token which is **<|im_end|>** for this model):
+
+
+
+### Understanding Token Selection: From Probabilities to Token Choices
+
+When the model needs to choose the next token, it starts with raw probabilities (called logits) for every word in its vocabulary. But how do we turn these probabilities into actual choices? Let's break down the process:
+
+
+
+1. **Raw Logits**: Think of these as the model's initial gut feelings about each possible next word
+2. **Temperature Control**: Like a creativity dial - higher settings (>1.0) make choices more random and creative, lower settings (<1.0) make them more focused and deterministic
+3. **Top-p (Nucleus) Sampling**: Instead of considering all possible words, we only look at the most likely ones that add up to our chosen probability threshold (e.g., top 90%)
+4. **Top-k Filtering**: An alternative approach where we only consider the k most likely next words
+
+### Managing Repetition: Keeping Output Fresh
+
+One common challenge with LLMs is their tendency to repeat themselves - much like a speaker who keeps returning to the same points. To address this, we use two types of penalties:
+
+1. **Presence Penalty**: A fixed penalty applied to any token that has appeared before, regardless of how often. This helps prevent the model from reusing the same words.
+2. **Frequency Penalty**: A scaling penalty that increases based on how often a token has been used. The more a word appears, the less likely it is to be chosen again.
+
+
+
+These penalties are applied early in the token selection process, adjusting the raw probabilities before other sampling strategies are applied. Think of them as gentle nudges encouraging the model to explore new vocabulary.
+
+### Controlling Generation Length: Setting Boundaries
+
+Just as a good story needs proper pacing and length, we need ways to control how much text our LLM generates. This is crucial for practical applications - whether we're generating a tweet-length response or a full blog post.
+
+We can control generation length in several ways:
+1. **Token Limits**: Setting minimum and maximum token counts
+2. **Stop Sequences**: Defining specific patterns that signal the end of generation
+3. **End-of-Sequence Detection**: Letting the model naturally conclude its response
+
+For example, if we want to generate a single paragraph, we might set a maximum of 100 tokens and use "\n\n" as a stop sequence. This ensures our output stays focused and appropriately sized for its purpose.
+
+
+
+### Beam Search: Looking Ahead for Better Coherence
+
+While the strategies we've discussed so far make decisions one token at a time, beam search takes a more holistic approach. Instead of committing to a single choice at each step, it explores multiple possible paths simultaneously - like a chess player thinking several moves ahead.
+
+
+
+Here's how it works:
+1. At each step, maintain multiple candidate sequences (typically 5-10)
+2. For each candidate, compute probabilities for the next token
+3. Keep only the most promising combinations of sequences and next tokens
+4. Continue this process until reaching the desired length or stop condition
+5. Select the sequence with the highest overall probability
+
+You can explore beam search visually here:
+
+
+
+This approach often produces more coherent and grammatically correct text, though it requires more computational resources than simpler methods.
+
+## Practical Challenges and Optimization
+
+As we wrap up our exploration of LLM inference, let's look at the practical challenges you'll face when deploying these models, and how to measure and optimize their performance.
+
+### Key Performance Metrics
+
+When working with LLMs, four critical metrics will shape your implementation decisions:
+
+1. **Time to First Token (TTFT)**: How quickly can you get the first response? This is crucial for user experience and is primarily affected by the prefill phase.
+2. **Time Per Output Token (TPOT)**: How fast can you generate subsequent tokens? This determines the overall generation speed.
+3. **Throughput**: How many requests can you handle simultaneously? This affects scaling and cost efficiency.
+4. **VRAM Usage**: How much GPU memory do you need? This often becomes the primary constraint in real-world applications.
+
+### The Context Length Challenge
+
+One of the most significant challenges in LLM inference is managing context length effectively. Longer contexts provide more information but come with substantial costs:
+
+- **Memory Usage**: Grows quadratically with context length
+- **Processing Speed**: Decreases linearly with longer contexts
+- **Resource Allocation**: Requires careful balancing of VRAM usage
+
+Recent models like [Qwen2.5-1M](https://huggingface.co/Qwen/Qwen2.5-14B-Instruct-1M) offer impressive 1M token context windows, but this comes at the cost of significantly slower inference times. The key is finding the right balance for your specific use case.
+
+
+
+
+
+
+ Input Text (Raw)
+
+
→
+
+ Tokenized Input
+
+
+
+
+ Context Window
(e.g., 4K tokens)
+
+
+
+
+
+
+ Memory Usage
∝ Length²
+
+
+
+
+ Processing Time
∝ Length
+
+
+
+
+
+
+### The KV Cache Optimization
+
+To address these challenges, one of the most powerful optimizations is KV (Key-Value) caching. This technique significantly improves inference speed by storing and reusing intermediate calculations. This optimization:
+- Reduces repeated calculations
+- Improves generation speed
+- Makes long-context generation practical
+
+The trade-off is additional memory usage, but the performance benefits usually far outweigh this cost.
+
+## Conclusion
+
+Understanding LLM inference is crucial for effectively deploying and optimizing these powerful models. We've covered the key components:
+
+- The fundamental role of attention and context
+- The two-phase inference process
+- Various sampling strategies for controlling generation
+- Practical challenges and optimizations
+
+By mastering these concepts, you'll be better equipped to build applications that leverage LLMs effectively and efficiently.
+
+Remember that the field of LLM inference is rapidly evolving, with new techniques and optimizations emerging regularly. Stay curious and keep experimenting with different approaches to find what works best for your specific use cases.
diff --git a/chapters/en/chapter1/9.mdx b/chapters/en/chapter1/9.mdx
index 13f448a32..bd2bdd5ea 100644
--- a/chapters/en/chapter1/9.mdx
+++ b/chapters/en/chapter1/9.mdx
@@ -1,32 +1,32 @@
-# Bias and limitations[[bias-and-limitations]]
-
-
-
-If your intent is to use a pretrained model or a fine-tuned version in production, please be aware that, while these models are powerful tools, they come with limitations. The biggest of these is that, to enable pretraining on large amounts of data, researchers often scrape all the content they can find, taking the best as well as the worst of what is available on the internet.
-
-To give a quick illustration, let's go back to the example of a `fill-mask` pipeline with the BERT model:
-
-```python
-from transformers import pipeline
-
-unmasker = pipeline("fill-mask", model="bert-base-uncased")
-result = unmasker("This man works as a [MASK].")
-print([r["token_str"] for r in result])
-
-result = unmasker("This woman works as a [MASK].")
-print([r["token_str"] for r in result])
-```
-
-```python out
-['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic']
-['nurse', 'waitress', 'teacher', 'maid', 'prostitute']
-```
-
-When asked to fill in the missing word in these two sentences, the model gives only one gender-free answer (waiter/waitress). The others are work occupations usually associated with one specific gender -- and yes, prostitute ended up in the top 5 possibilities the model associates with "woman" and "work." This happens even though BERT is one of the rare Transformer models not built by scraping data from all over the internet, but rather using apparently neutral data (it's trained on the [English Wikipedia](https://huggingface.co/datasets/wikipedia) and [BookCorpus](https://huggingface.co/datasets/bookcorpus) datasets).
-
-When you use these tools, you therefore need to keep in the back of your mind that the original model you are using could very easily generate sexist, racist, or homophobic content. Fine-tuning the model on your data won't make this intrinsic bias disappear.
+# Bias and limitations[[bias-and-limitations]]
+
+
+
+If your intent is to use a pretrained model or a fine-tuned version in production, please be aware that, while these models are powerful tools, they come with limitations. The biggest of these is that, to enable pretraining on large amounts of data, researchers often scrape all the content they can find, taking the best as well as the worst of what is available on the internet.
+
+To give a quick illustration, let's go back to the example of a `fill-mask` pipeline with the BERT model:
+
+```python
+from transformers import pipeline
+
+unmasker = pipeline("fill-mask", model="bert-base-uncased")
+result = unmasker("This man works as a [MASK].")
+print([r["token_str"] for r in result])
+
+result = unmasker("This woman works as a [MASK].")
+print([r["token_str"] for r in result])
+```
+
+```python out
+['lawyer', 'carpenter', 'doctor', 'waiter', 'mechanic']
+['nurse', 'waitress', 'teacher', 'maid', 'prostitute']
+```
+
+When asked to fill in the missing word in these two sentences, the model gives only one gender-free answer (waiter/waitress). The others are work occupations usually associated with one specific gender -- and yes, prostitute ended up in the top 5 possibilities the model associates with "woman" and "work." This happens even though BERT is one of the rare Transformer models not built by scraping data from all over the internet, but rather using apparently neutral data (it's trained on the [English Wikipedia](https://huggingface.co/datasets/wikipedia) and [BookCorpus](https://huggingface.co/datasets/bookcorpus) datasets).
+
+When you use these tools, you therefore need to keep in the back of your mind that the original model you are using could very easily generate sexist, racist, or homophobic content. Fine-tuning the model on your data won't make this intrinsic bias disappear.
diff --git a/chapters/en/chapter10/1.mdx b/chapters/en/chapter10/1.mdx
index 4301620cc..718b99390 100644
--- a/chapters/en/chapter10/1.mdx
+++ b/chapters/en/chapter10/1.mdx
@@ -1,26 +1,26 @@
-# Introduction to Argilla[[introduction-to-argilla]]
-
-
-
-In Chapter 5 you learnt how to build a dataset using the 🤗 Datasets library and in Chapter 6 you explored how to fine-tune models for some common NLP tasks. In this chapter, you will learn how to use [Argilla](https://argilla.io) to **annotate and curate datasets** that you can use to train and evaluate your models.
-
-The key to training models that perform well is to have high-quality data. Although there are some good datasets in the Hub that you could use to train and evaluate your models, these may not be relevant for your specific application or use case. In this scenario, you may want to build and curate a dataset of your own. Argilla will help you to do this efficiently.
-
-
-
-With Argilla you can:
-
-- turn unstructured data into **structured data** to be used in NLP tasks.
-- curate a dataset to go from a low-quality dataset to a **high-quality dataset**.
-- gather **human feedback** for LLMs and multi-modal models.
-- invite experts to collaborate with you in Argilla, or crowdsource annotations!
-
-Here are some of the things that you will learn in this chapter:
-
-- How to set up your own Argilla instance.
-- How to load a dataset and configure it based on some popular NLP tasks.
-- How to use the Argilla UI to annotate your dataset.
-- How to use your curated dataset and export it to the Hub.
+# Introduction to Argilla[[introduction-to-argilla]]
+
+
+
+In Chapter 5 you learnt how to build a dataset using the 🤗 Datasets library and in Chapter 6 you explored how to fine-tune models for some common NLP tasks. In this chapter, you will learn how to use [Argilla](https://argilla.io) to **annotate and curate datasets** that you can use to train and evaluate your models.
+
+The key to training models that perform well is to have high-quality data. Although there are some good datasets in the Hub that you could use to train and evaluate your models, these may not be relevant for your specific application or use case. In this scenario, you may want to build and curate a dataset of your own. Argilla will help you to do this efficiently.
+
+
+
+With Argilla you can:
+
+- turn unstructured data into **structured data** to be used in NLP tasks.
+- curate a dataset to go from a low-quality dataset to a **high-quality dataset**.
+- gather **human feedback** for LLMs and multi-modal models.
+- invite experts to collaborate with you in Argilla, or crowdsource annotations!
+
+Here are some of the things that you will learn in this chapter:
+
+- How to set up your own Argilla instance.
+- How to load a dataset and configure it based on some popular NLP tasks.
+- How to use the Argilla UI to annotate your dataset.
+- How to use your curated dataset and export it to the Hub.
diff --git a/chapters/en/chapter10/2.mdx b/chapters/en/chapter10/2.mdx
index 447ef04aa..486081796 100644
--- a/chapters/en/chapter10/2.mdx
+++ b/chapters/en/chapter10/2.mdx
@@ -1,55 +1,55 @@
-# Set up your Argilla instance[[set-up-your-argilla-instance]]
-
-
-
-To start using Argilla, you will need to set up your own Argilla instance first. Then you will need to install the Python SDK so that you can manage Argilla using Python code.
-
-## Deploy the Argilla UI
-
-The easiest way to set up your Argilla instance is through Hugging Face Spaces. To create your Argilla Space, simply follow [this form](https://huggingface.co/new-space?template=argilla%2Fargilla-template-space). If you need further guidance, check the [Argilla quickstart](https://docs.argilla.io/latest/getting_started/quickstart/).
-
-
->[!WARNING]
-> ⚠️ You may want to enable **Persistent storage** so the data isn't lost if the Space is paused or restarted.
-> You can do that from the Settings of your Space.
-
-Once Argilla is up and running, you can log in with your credentials.
-
-## Install and connect the Python SDK
-
-Now you can go to your Python environment or notebook and install the argilla library:
-
-`!pip install argilla`
-
-Let's connect with our Argilla instance. To do that you will need the following information:
-
-- **Your API URL**: This is the URL where Argilla is running. If you are using a Space, you can open the Space, click on the three dots in the top right corner, then "Embed this Space" and copy the **Direct URL**. It should look something like `https://..hf.space`.
-- **Your API key**: To get your key, log in to your Argilla instance and go to "My Settings", then copy the API key.
-- **Your HF token**: If your Space is private, you will need to an Access Token in your Hugging Face Hub account with writing permissions.
-
-```python
-import argilla as rg
-
-HF_TOKEN = "..." # only for private spaces
-
-client = rg.Argilla(
- api_url="...",
- api_key="...",
- headers={"Authorization": f"Bearer {HF_TOKEN}"}, # only for private spaces
-)
-```
-
-To check that everything is working properly, we'll call `me`. This should return our user:
-
-```python
-client.me
-```
-
-If this worked, your Argilla instance is up and running and you're connected to it! Congrats!
-
-We can now get started with loading our first dataset to Argilla.
+# Set up your Argilla instance[[set-up-your-argilla-instance]]
+
+
+
+To start using Argilla, you will need to set up your own Argilla instance first. Then you will need to install the Python SDK so that you can manage Argilla using Python code.
+
+## Deploy the Argilla UI
+
+The easiest way to set up your Argilla instance is through Hugging Face Spaces. To create your Argilla Space, simply follow [this form](https://huggingface.co/new-space?template=argilla%2Fargilla-template-space). If you need further guidance, check the [Argilla quickstart](https://docs.argilla.io/latest/getting_started/quickstart/).
+
+
+>[!WARNING]
+> ⚠️ You may want to enable **Persistent storage** so the data isn't lost if the Space is paused or restarted.
+> You can do that from the Settings of your Space.
+
+Once Argilla is up and running, you can log in with your credentials.
+
+## Install and connect the Python SDK
+
+Now you can go to your Python environment or notebook and install the argilla library:
+
+`!pip install argilla`
+
+Let's connect with our Argilla instance. To do that you will need the following information:
+
+- **Your API URL**: This is the URL where Argilla is running. If you are using a Space, you can open the Space, click on the three dots in the top right corner, then "Embed this Space" and copy the **Direct URL**. It should look something like `https://..hf.space`.
+- **Your API key**: To get your key, log in to your Argilla instance and go to "My Settings", then copy the API key.
+- **Your HF token**: If your Space is private, you will need to an Access Token in your Hugging Face Hub account with writing permissions.
+
+```python
+import argilla as rg
+
+HF_TOKEN = "..." # only for private spaces
+
+client = rg.Argilla(
+ api_url="...",
+ api_key="...",
+ headers={"Authorization": f"Bearer {HF_TOKEN}"}, # only for private spaces
+)
+```
+
+To check that everything is working properly, we'll call `me`. This should return our user:
+
+```python
+client.me
+```
+
+If this worked, your Argilla instance is up and running and you're connected to it! Congrats!
+
+We can now get started with loading our first dataset to Argilla.
diff --git a/chapters/en/chapter10/3.mdx b/chapters/en/chapter10/3.mdx
index be70cc469..359e85015 100644
--- a/chapters/en/chapter10/3.mdx
+++ b/chapters/en/chapter10/3.mdx
@@ -1,108 +1,108 @@
-# Load your dataset to Argilla[[load-your-dataset-to-argilla]]
-
-
-
-Depending on the NLP task that you're working with and the specific use case or application, your data and the annotation task will look differently. For this section of the course, we'll use [a dataset collecting news](https://huggingface.co/datasets/SetFit/ag_news) to complete two tasks: a text classification on the topic of each text and a token classification to identify the named entities mentioned.
-
-
-
-It is possible to import datasets from the Hub using the Argilla UI directly, but we'll be using the SDK to learn how we can make further edits to the data if needed.
-
-## Configure your dataset
-
-The first step is to connect to our Argilla instance as we did in the previous section:
-
-```python
-import argilla as rg
-
-HF_TOKEN = "..." # only for private spaces
-
-client = rg.Argilla(
- api_url="...",
- api_key="...",
- headers={"Authorization": f"Bearer {HF_TOKEN}"}, # only for private spaces
-)
-```
-
-We can now think about the settings of our dataset in Argilla. These represent the annotation task we'll do over our data. First, we can load the dataset from the Hub and inspect its features, so that we can make sure that we configure the dataset correctly.
-
-```python
-from datasets import load_dataset
-
-data = load_dataset("SetFit/ag_news", split="train")
-data.features
-```
-
-These are the features of our dataset:
-
-```python out
-{'text': Value(dtype='string', id=None),
- 'label': Value(dtype='int64', id=None),
- 'label_text': Value(dtype='string', id=None)}
-```
-
-It contains a `text` and also some initial labels for the text classification. We'll add those to our dataset settings together with a `spans` question for the named entities:
-
-```python
-settings = rg.Settings(
- fields=[rg.TextField(name="text")],
- questions=[
- rg.LabelQuestion(
- name="label", title="Classify the text:", labels=data.unique("label_text")
- ),
- rg.SpanQuestion(
- name="entities",
- title="Highlight all the entities in the text:",
- labels=["PERSON", "ORG", "LOC", "EVENT"],
- field="text",
- ),
- ],
-)
-```
-
-Let's dive a bit deeper into what these settings mean. First, we've defined **fields**, these include the information that we'll be annotating. In this case, we only have one field and it comes in the form of a text, so we've choosen a `TextField`.
-
-Then, we define **questions** that represent the tasks that we want to perform on our data:
-
-- For the text classification task we've chosen a `LabelQuestion` and we used the unique values of the `label_text` column as our labels, to make sure that the question is compatible with the labels that already exist in the dataset.
-- For the token classification task, we'll need a `SpanQuestion`. We've defined a set of labels that we'll be using for that task, plus the field on which we'll be drawing the spans.
-
-To learn more about all the available types of fields and questions and other advanced settings, like metadata and vectors, go to the [Argilla docs](https://docs.argilla.io/latest/how_to_guides/dataset/#define-dataset-settings).
-
-## Upload the dataset
-
-Now that we've defined some settings, we can create the dataset:
-
-```python
-dataset = rg.Dataset(name="ag_news", settings=settings)
-
-dataset.create()
-```
-
-The dataset now appears in our Argilla instance, but you will see that it's empty:
-
-
-
-Now we need to add the records that we'll be annotating i.e., the rows in our dataset. To do that, we'll simply need to log the data as records and provide a mapping for those elements that don't have the same name in the hub and Argilla datasets:
-
-```python
-dataset.records.log(data, mapping={"label_text": "label"})
-```
-
-In our mapping, we've specified that the `label_text` column in the dataset should be mapped to the question with the name `label`. In this way, we'll use the existing labels in the dataset as pre-annotations so we can annotate faster.
-
-While the records continue to log, you can already start working with your dataset in the Argilla UI. At this point, it should look like this:
-
-
-
-Now our dataset is ready to start annotating!
+# Load your dataset to Argilla[[load-your-dataset-to-argilla]]
+
+
+
+Depending on the NLP task that you're working with and the specific use case or application, your data and the annotation task will look differently. For this section of the course, we'll use [a dataset collecting news](https://huggingface.co/datasets/SetFit/ag_news) to complete two tasks: a text classification on the topic of each text and a token classification to identify the named entities mentioned.
+
+
+
+It is possible to import datasets from the Hub using the Argilla UI directly, but we'll be using the SDK to learn how we can make further edits to the data if needed.
+
+## Configure your dataset
+
+The first step is to connect to our Argilla instance as we did in the previous section:
+
+```python
+import argilla as rg
+
+HF_TOKEN = "..." # only for private spaces
+
+client = rg.Argilla(
+ api_url="...",
+ api_key="...",
+ headers={"Authorization": f"Bearer {HF_TOKEN}"}, # only for private spaces
+)
+```
+
+We can now think about the settings of our dataset in Argilla. These represent the annotation task we'll do over our data. First, we can load the dataset from the Hub and inspect its features, so that we can make sure that we configure the dataset correctly.
+
+```python
+from datasets import load_dataset
+
+data = load_dataset("SetFit/ag_news", split="train")
+data.features
+```
+
+These are the features of our dataset:
+
+```python out
+{'text': Value(dtype='string', id=None),
+ 'label': Value(dtype='int64', id=None),
+ 'label_text': Value(dtype='string', id=None)}
+```
+
+It contains a `text` and also some initial labels for the text classification. We'll add those to our dataset settings together with a `spans` question for the named entities:
+
+```python
+settings = rg.Settings(
+ fields=[rg.TextField(name="text")],
+ questions=[
+ rg.LabelQuestion(
+ name="label", title="Classify the text:", labels=data.unique("label_text")
+ ),
+ rg.SpanQuestion(
+ name="entities",
+ title="Highlight all the entities in the text:",
+ labels=["PERSON", "ORG", "LOC", "EVENT"],
+ field="text",
+ ),
+ ],
+)
+```
+
+Let's dive a bit deeper into what these settings mean. First, we've defined **fields**, these include the information that we'll be annotating. In this case, we only have one field and it comes in the form of a text, so we've choosen a `TextField`.
+
+Then, we define **questions** that represent the tasks that we want to perform on our data:
+
+- For the text classification task we've chosen a `LabelQuestion` and we used the unique values of the `label_text` column as our labels, to make sure that the question is compatible with the labels that already exist in the dataset.
+- For the token classification task, we'll need a `SpanQuestion`. We've defined a set of labels that we'll be using for that task, plus the field on which we'll be drawing the spans.
+
+To learn more about all the available types of fields and questions and other advanced settings, like metadata and vectors, go to the [Argilla docs](https://docs.argilla.io/latest/how_to_guides/dataset/#define-dataset-settings).
+
+## Upload the dataset
+
+Now that we've defined some settings, we can create the dataset:
+
+```python
+dataset = rg.Dataset(name="ag_news", settings=settings)
+
+dataset.create()
+```
+
+The dataset now appears in our Argilla instance, but you will see that it's empty:
+
+
+
+Now we need to add the records that we'll be annotating i.e., the rows in our dataset. To do that, we'll simply need to log the data as records and provide a mapping for those elements that don't have the same name in the hub and Argilla datasets:
+
+```python
+dataset.records.log(data, mapping={"label_text": "label"})
+```
+
+In our mapping, we've specified that the `label_text` column in the dataset should be mapped to the question with the name `label`. In this way, we'll use the existing labels in the dataset as pre-annotations so we can annotate faster.
+
+While the records continue to log, you can already start working with your dataset in the Argilla UI. At this point, it should look like this:
+
+
+
+Now our dataset is ready to start annotating!
diff --git a/chapters/en/chapter10/4.mdx b/chapters/en/chapter10/4.mdx
index 59c845502..f7f6e94b3 100644
--- a/chapters/en/chapter10/4.mdx
+++ b/chapters/en/chapter10/4.mdx
@@ -1,44 +1,44 @@
-# Annotate your dataset[[annotate-your-dataset]]
-
-
-
-Now it is time to start working from the Argilla UI to annotate our dataset.
-
-## Align your team with annotation guidelines
-
-Before you start annotating your dataset, it is always good practice to write some guidelines, especially if you're working as part of a team. This will help you align on the task and the use of the different labels, and resolve questions or conflicts when they come up.
-
-In Argilla, you can go to your dataset settings page in the UI and modify the guidelines and the descriptions of your questions to help with alignment.
-
-
-
-If you want to dive deeper into the topic of how to write good guidelines, we recommend reading [this blogpost](https://argilla.io/blog/annotation-guidelines-practices) and the bibliographical references mentioned there.
-
-## Distribute the task
-
-In the dataset settings page, you can also change the dataset distribution settings. This will help you annotate more efficiently when you're working as part of a team. The default value for the minimum submitted responses is 1, meaning that as soon as a record has 1 submitted response it will be considered complete and count towards the progress in your dataset.
-
-Sometimes, you want to have more than one submitted response per record, for example, if you want to analyze the inter-annotator agreement in your task. In that case, make sure to change this setting to a higher number, but always smaller or equal to the total number of annotators. If you're working on the task alone, you want this setting to be 1.
-
-## Annotate records
-
->[!TIP]
->💡 If you are deploying Argilla in a Hugging Face Space, any team members will be able to log in using the Hugging Face OAuth. Otherwise, you may need to create users for them following [this guide](https://docs.argilla.io/latest/how_to_guides/user/).
-
-When you open your dataset, you will realize that the first question is already filled in with some suggested labels. That's because in the previous section we mapped our question called `label` to the `label_text` column in the dataset, so that we simply need to review and correct the already existing labels:
-
-
-
-For the token classification, we'll need to add all labels manually, as we didn't include any suggestions. This is how it might look after the span annotations:
-
-
-
-As you move through the different records, there are different actions you can take:
-- submit your responses, once you're done with the record.
-- save them as a draft, in case you want to come back to them later.
-- discard them, if the record souldn't be part of the dataset or you won't give responses to it.
-
-In the next section, you will learn how you can export and use those annotations.
+# Annotate your dataset[[annotate-your-dataset]]
+
+
+
+Now it is time to start working from the Argilla UI to annotate our dataset.
+
+## Align your team with annotation guidelines
+
+Before you start annotating your dataset, it is always good practice to write some guidelines, especially if you're working as part of a team. This will help you align on the task and the use of the different labels, and resolve questions or conflicts when they come up.
+
+In Argilla, you can go to your dataset settings page in the UI and modify the guidelines and the descriptions of your questions to help with alignment.
+
+
+
+If you want to dive deeper into the topic of how to write good guidelines, we recommend reading [this blogpost](https://argilla.io/blog/annotation-guidelines-practices) and the bibliographical references mentioned there.
+
+## Distribute the task
+
+In the dataset settings page, you can also change the dataset distribution settings. This will help you annotate more efficiently when you're working as part of a team. The default value for the minimum submitted responses is 1, meaning that as soon as a record has 1 submitted response it will be considered complete and count towards the progress in your dataset.
+
+Sometimes, you want to have more than one submitted response per record, for example, if you want to analyze the inter-annotator agreement in your task. In that case, make sure to change this setting to a higher number, but always smaller or equal to the total number of annotators. If you're working on the task alone, you want this setting to be 1.
+
+## Annotate records
+
+>[!TIP]
+>💡 If you are deploying Argilla in a Hugging Face Space, any team members will be able to log in using the Hugging Face OAuth. Otherwise, you may need to create users for them following [this guide](https://docs.argilla.io/latest/how_to_guides/user/).
+
+When you open your dataset, you will realize that the first question is already filled in with some suggested labels. That's because in the previous section we mapped our question called `label` to the `label_text` column in the dataset, so that we simply need to review and correct the already existing labels:
+
+
+
+For the token classification, we'll need to add all labels manually, as we didn't include any suggestions. This is how it might look after the span annotations:
+
+
+
+As you move through the different records, there are different actions you can take:
+- submit your responses, once you're done with the record.
+- save them as a draft, in case you want to come back to them later.
+- discard them, if the record souldn't be part of the dataset or you won't give responses to it.
+
+In the next section, you will learn how you can export and use those annotations.
diff --git a/chapters/en/chapter10/5.mdx b/chapters/en/chapter10/5.mdx
index 7a3b01f1b..a8596b8f0 100644
--- a/chapters/en/chapter10/5.mdx
+++ b/chapters/en/chapter10/5.mdx
@@ -1,69 +1,69 @@
-# Use your annotated dataset[[use-your-annotated-dataset]]
-
-
-
-We will learn now how to export and use the annotated data that we have in Argilla.
-
-## Load the dataset
-
-First, we'll need to make sure that we're connected to our Argilla instance as in the previous steps:
-
-```python
-import argilla as rg
-
-HF_TOKEN = "..." # only for private spaces
-
-client = rg.Argilla(
- api_url="...",
- api_key="...",
- headers={"Authorization": f"Bearer {HF_TOKEN}"}, # only for private spaces
-)
-```
-
-And now, we'll load the dataset that we'll be working with:
-
-```python
-dataset = client.datasets(name="ag_news")
-```
-
-Loading the dataset and calling its records with `dataset.records` is enough to start using your dataset and records for your own purposes and pipelines. However, we'll also learn how to do a few optional operations, like filtering the records and exporting your dataset to the Hugging Face Hub.
-
-## Filter the dataset
-
-Sometimes you only want to use the records that have been completed, so we will first filter the records in our dataset based on their status:
-
-```python
-status_filter = rg.Query(filter=rg.Filter([("status", "==", "completed")]))
-
-filtered_records = dataset.records(status_filter)
-```
-
->[!TIP]
->⚠️ Note that the records with `completed` status (i.e., records that meet the minimum submitted responses configured in the task distribution settings) could have more than one response and that each response can have any status from `submitted`, `draft` or `discarded`.
-
-Learn more about querying and filtering records in the [Argilla docs](https://docs.argilla.io/latest/how_to_guides/query/).
-
-## Export to the Hub
-
-We can now export our annotations to the Hugging Face Hub, so we can share them with others. To do this, we'll need to convert the records into a 🤗 Dataset and then push it to the Hub:
-
-```python
-filtered_records.to_datasets().push_to_hub("argilla/ag_news_annotated")
-```
-
-Alternatively, we can export directly the complete Argilla dataset (including pending records) like this:
-
-```python
-dataset.to_hub(repo_id="argilla/ag_news_annotated")
-```
-
-This is an interesting choice in case others want to open the dataset in their Argilla instances, as the settings are automatically saved and they can simply import the full dataset using a single line of code:
-
-```python
-dataset = rg.Dataset.from_hub(repo_id="argilla/ag_news_annotated")
-```
+# Use your annotated dataset[[use-your-annotated-dataset]]
+
+
+
+We will learn now how to export and use the annotated data that we have in Argilla.
+
+## Load the dataset
+
+First, we'll need to make sure that we're connected to our Argilla instance as in the previous steps:
+
+```python
+import argilla as rg
+
+HF_TOKEN = "..." # only for private spaces
+
+client = rg.Argilla(
+ api_url="...",
+ api_key="...",
+ headers={"Authorization": f"Bearer {HF_TOKEN}"}, # only for private spaces
+)
+```
+
+And now, we'll load the dataset that we'll be working with:
+
+```python
+dataset = client.datasets(name="ag_news")
+```
+
+Loading the dataset and calling its records with `dataset.records` is enough to start using your dataset and records for your own purposes and pipelines. However, we'll also learn how to do a few optional operations, like filtering the records and exporting your dataset to the Hugging Face Hub.
+
+## Filter the dataset
+
+Sometimes you only want to use the records that have been completed, so we will first filter the records in our dataset based on their status:
+
+```python
+status_filter = rg.Query(filter=rg.Filter([("status", "==", "completed")]))
+
+filtered_records = dataset.records(status_filter)
+```
+
+>[!TIP]
+>⚠️ Note that the records with `completed` status (i.e., records that meet the minimum submitted responses configured in the task distribution settings) could have more than one response and that each response can have any status from `submitted`, `draft` or `discarded`.
+
+Learn more about querying and filtering records in the [Argilla docs](https://docs.argilla.io/latest/how_to_guides/query/).
+
+## Export to the Hub
+
+We can now export our annotations to the Hugging Face Hub, so we can share them with others. To do this, we'll need to convert the records into a 🤗 Dataset and then push it to the Hub:
+
+```python
+filtered_records.to_datasets().push_to_hub("argilla/ag_news_annotated")
+```
+
+Alternatively, we can export directly the complete Argilla dataset (including pending records) like this:
+
+```python
+dataset.to_hub(repo_id="argilla/ag_news_annotated")
+```
+
+This is an interesting choice in case others want to open the dataset in their Argilla instances, as the settings are automatically saved and they can simply import the full dataset using a single line of code:
+
+```python
+dataset = rg.Dataset.from_hub(repo_id="argilla/ag_news_annotated")
+```
diff --git a/chapters/en/chapter10/6.mdx b/chapters/en/chapter10/6.mdx
index a65b9e3c8..91ba0b45f 100644
--- a/chapters/en/chapter10/6.mdx
+++ b/chapters/en/chapter10/6.mdx
@@ -1,20 +1,20 @@
-# Argilla, check![[argilla-check]]
-
-
-
-That's all! Congrats! 👏
-
-In this chapter, you learnt the basic steps to:
-- set up Argilla.
-- annotate to improve the quality of your dataset.
-- adapt an existing dataset and re-use it for a different NLP task.
-- share your annotated dataset with the community in the Hugging Face Hub.
-
-## What's next?
-- Check more step-by-step tutorials for other popular tasks in the [tutorials page](https://docs.argilla.io/latest/tutorials/).
-- You can also explore other examples of datasets in this [demo](https://demo.argilla.io/sign-in?auth=ZGVtbzoxMjM0NTY3OA==).
-- If you'd like to keep learning about Argilla and more advanced features, check the [Argilla documentation](https://docs.argilla.io/latest/).
+# Argilla, check![[argilla-check]]
+
+
+
+That's all! Congrats! 👏
+
+In this chapter, you learnt the basic steps to:
+- set up Argilla.
+- annotate to improve the quality of your dataset.
+- adapt an existing dataset and re-use it for a different NLP task.
+- share your annotated dataset with the community in the Hugging Face Hub.
+
+## What's next?
+- Check more step-by-step tutorials for other popular tasks in the [tutorials page](https://docs.argilla.io/latest/tutorials/).
+- You can also explore other examples of datasets in this [demo](https://demo.argilla.io/sign-in?auth=ZGVtbzoxMjM0NTY3OA==).
+- If you'd like to keep learning about Argilla and more advanced features, check the [Argilla documentation](https://docs.argilla.io/latest/).
\ No newline at end of file
diff --git a/chapters/en/chapter10/7.mdx b/chapters/en/chapter10/7.mdx
index 39214ed70..a704fb398 100644
--- a/chapters/en/chapter10/7.mdx
+++ b/chapters/en/chapter10/7.mdx
@@ -1,186 +1,186 @@
-
-
-# End-of-chapter quiz[[end-of-chapter-quiz]]
-
-
-
-Let's test what you learned in this chapter!
-
-### 1. What can you use Argilla for?
-
-
-
-### 2. Argilla ONLY works in the Hugging Face Spaces and with Hugging Face Datasets.
-
-
-
-### 3. You need a Hugging Face token to connect the Python SDK to your Argilla server.
-
-
-
-### 4. What are **fields** in Argilla? How many fields can you use?
-
-
-
-### 5. What's the best type of question for a token classification task?
-
-
-
-### 6. What is the purpose of the "Save as draft" button?
-
-
-
-### 7. Argilla does not offer suggested labels automatically, you need to provide that data yourself.
-
-
-
-### 8. Select all the necessary steps to export an Argilla dataset in full to the Hub:
-
-client= rg.Argilla(api_url='...', api_key='...')",
- explain: "Yes, to interact with your server you'll need to instantiate it first.",
- correct: true
- },
- {
- text: "Import the dataset from the hub: dataset = rg.Dataset.from_hub(repo_id='argilla/ag_news_annotated')",
- explain: "No. This is to import a dataset from the Hub into your Argilla instance.",
- },
- {
- text: "Load the dataset: dataset = client.datasets(name='my_dataset')",
- explain: "Yes, you'll need this for further operations",
- correct: true
- },
- {
- text: "Convert the Argilla dataset into a Datasets dataset: dataset = dataset.to_datasets()",
- explain: "This is not needed if you export the full dataset. Argilla will take care of this for you. However, you might need it if you're working with a subset of records."
- },
- {
- text: "Use the to_hub method to export the dataset: dataset.to_hub(repo_id='my_username/dataset_name')",
- explain: "This will push the dataset to the indicated repo id, and create a new repo if it doesn't exist.",
- correct: true
- },
- ]}
-/>
+
+
+# End-of-chapter quiz[[end-of-chapter-quiz]]
+
+
+
+Let's test what you learned in this chapter!
+
+### 1. What can you use Argilla for?
+
+
+
+### 2. Argilla ONLY works in the Hugging Face Spaces and with Hugging Face Datasets.
+
+
+
+### 3. You need a Hugging Face token to connect the Python SDK to your Argilla server.
+
+
+
+### 4. What are **fields** in Argilla? How many fields can you use?
+
+
+
+### 5. What's the best type of question for a token classification task?
+
+
+
+### 6. What is the purpose of the "Save as draft" button?
+
+
+
+### 7. Argilla does not offer suggested labels automatically, you need to provide that data yourself.
+
+
+
+### 8. Select all the necessary steps to export an Argilla dataset in full to the Hub:
+
+client= rg.Argilla(api_url='...', api_key='...')",
+ explain: "Yes, to interact with your server you'll need to instantiate it first.",
+ correct: true
+ },
+ {
+ text: "Import the dataset from the hub: dataset = rg.Dataset.from_hub(repo_id='argilla/ag_news_annotated')",
+ explain: "No. This is to import a dataset from the Hub into your Argilla instance.",
+ },
+ {
+ text: "Load the dataset: dataset = client.datasets(name='my_dataset')",
+ explain: "Yes, you'll need this for further operations",
+ correct: true
+ },
+ {
+ text: "Convert the Argilla dataset into a Datasets dataset: dataset = dataset.to_datasets()",
+ explain: "This is not needed if you export the full dataset. Argilla will take care of this for you. However, you might need it if you're working with a subset of records."
+ },
+ {
+ text: "Use the to_hub method to export the dataset: dataset.to_hub(repo_id='my_username/dataset_name')",
+ explain: "This will push the dataset to the indicated repo id, and create a new repo if it doesn't exist.",
+ correct: true
+ },
+ ]}
+/>
diff --git a/chapters/en/chapter11/1.mdx b/chapters/en/chapter11/1.mdx
index e6da9f28d..157a67bb7 100644
--- a/chapters/en/chapter11/1.mdx
+++ b/chapters/en/chapter11/1.mdx
@@ -1,32 +1,32 @@
-# Supervised Fine-Tuning
-
-In [Chapter 2 Section 2](/course/chapter2/2), we saw that generative language models can be fine-tuned on specific tasks like summarization and question answering. However, nowadays it is far more common to fine-tune language models on a broad range of tasks simultaneously; a method known as supervised fine-tuning (SFT). This process helps models become more versatile and capable of handling diverse use cases. Most LLMs that people interact with on platforms like ChatGPT have undergone SFT to make them more helpful and aligned with human preferences. We will separate this chapter into four sections:
-
-## 1️⃣ Chat Templates
-
-Chat templates structure interactions between users and AI models, ensuring consistent and contextually appropriate responses. They include components like system prompts and role-based messages.
-
-## 2️⃣ Supervised Fine-Tuning
-
-Supervised Fine-Tuning (SFT) is a critical process for adapting pre-trained language models to specific tasks. It involves training the model on a task-specific dataset with labeled examples. For a detailed guide on SFT, including key steps and best practices, see [the supervised fine-tuning section of the TRL documentation](https://huggingface.co/docs/trl/en/sft_trainer).
-
-## 3️⃣ Low Rank Adaptation (LoRA)
-
-Low Rank Adaptation (LoRA) is a technique for fine-tuning language models by adding low-rank matrices to the model's layers. This allows for efficient fine-tuning while preserving the model's pre-trained knowledge. One of the key benefits of LoRA is the significant memory savings it offers, making it possible to fine-tune large models on hardware with limited resources.
-
-## 4️⃣ Evaluation
-
-Evaluation is a crucial step in the fine-tuning process. It allows us to measure the performance of the model on a task-specific dataset.
-
-> [!TIP]
-> ⚠️ In order to benefit from all features available with the Model Hub and 🤗 Transformers, we recommend creating an account.
-
-## References
-
-- [Transformers documentation on chat templates](https://huggingface.co/docs/transformers/main/en/chat_templating)
-- [Script for Supervised Fine-Tuning in TRL](https://github.com/huggingface/trl/blob/main/trl/scripts/sft.py)
-- [`SFTTrainer` in TRL](https://huggingface.co/docs/trl/main/en/sft_trainer)
-- [Direct Preference Optimization Paper](https://arxiv.org/abs/2305.18290)
-- [Supervised Fine-Tuning with TRL](https://huggingface.co/docs/trl/sft_trainer)
-- [How to fine-tune Google Gemma with ChatML and Hugging Face TRL](https://github.com/huggingface/alignment-handbook)
-- [Fine-tuning LLM to Generate Persian Product Catalogs in JSON Format](https://huggingface.co/learn/cookbook/en/fine_tuning_llm_to_generate_persian_product_catalogs_in_json_format)
+# Supervised Fine-Tuning
+
+In [Chapter 2 Section 2](/course/chapter2/2), we saw that generative language models can be fine-tuned on specific tasks like summarization and question answering. However, nowadays it is far more common to fine-tune language models on a broad range of tasks simultaneously; a method known as supervised fine-tuning (SFT). This process helps models become more versatile and capable of handling diverse use cases. Most LLMs that people interact with on platforms like ChatGPT have undergone SFT to make them more helpful and aligned with human preferences. We will separate this chapter into four sections:
+
+## 1️⃣ Chat Templates
+
+Chat templates structure interactions between users and AI models, ensuring consistent and contextually appropriate responses. They include components like system prompts and role-based messages.
+
+## 2️⃣ Supervised Fine-Tuning
+
+Supervised Fine-Tuning (SFT) is a critical process for adapting pre-trained language models to specific tasks. It involves training the model on a task-specific dataset with labeled examples. For a detailed guide on SFT, including key steps and best practices, see [the supervised fine-tuning section of the TRL documentation](https://huggingface.co/docs/trl/en/sft_trainer).
+
+## 3️⃣ Low Rank Adaptation (LoRA)
+
+Low Rank Adaptation (LoRA) is a technique for fine-tuning language models by adding low-rank matrices to the model's layers. This allows for efficient fine-tuning while preserving the model's pre-trained knowledge. One of the key benefits of LoRA is the significant memory savings it offers, making it possible to fine-tune large models on hardware with limited resources.
+
+## 4️⃣ Evaluation
+
+Evaluation is a crucial step in the fine-tuning process. It allows us to measure the performance of the model on a task-specific dataset.
+
+> [!TIP]
+> ⚠️ In order to benefit from all features available with the Model Hub and 🤗 Transformers, we recommend creating an account.
+
+## References
+
+- [Transformers documentation on chat templates](https://huggingface.co/docs/transformers/main/en/chat_templating)
+- [Script for Supervised Fine-Tuning in TRL](https://github.com/huggingface/trl/blob/main/trl/scripts/sft.py)
+- [`SFTTrainer` in TRL](https://huggingface.co/docs/trl/main/en/sft_trainer)
+- [Direct Preference Optimization Paper](https://arxiv.org/abs/2305.18290)
+- [Supervised Fine-Tuning with TRL](https://huggingface.co/docs/trl/sft_trainer)
+- [How to fine-tune Google Gemma with ChatML and Hugging Face TRL](https://github.com/huggingface/alignment-handbook)
+- [Fine-tuning LLM to Generate Persian Product Catalogs in JSON Format](https://huggingface.co/learn/cookbook/en/fine_tuning_llm_to_generate_persian_product_catalogs_in_json_format)
diff --git a/chapters/en/chapter11/2.mdx b/chapters/en/chapter11/2.mdx
index 08b03f700..00069dcfb 100644
--- a/chapters/en/chapter11/2.mdx
+++ b/chapters/en/chapter11/2.mdx
@@ -1,249 +1,249 @@
-
-
-# Chat Templates
-
-## Introduction
-
-Chat templates are essential for structuring interactions between language models and users. Whether you're building a simple chatbot or a complex AI agent, understanding how to properly format your conversations is crucial for getting the best results from your model. In this guide, we'll explore what chat templates are, why they matter, and how to use them effectively.
-
-> [!TIP]
-> Chat templates are crucial for:
-> - Maintaining consistent conversation structure
-> - Ensuring proper role identification
-> - Managing context across multiple turns
-> - Supporting advanced features like tool use
-
-## Model Types and Templates
-
-### Base Models vs Instruct Models
-A base model is trained on raw text data to predict the next token, while an instruct model is fine-tuned specifically to follow instructions and engage in conversations. For example, [`SmolLM2-135M`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M) is a base model, while [`SmolLM2-135M-Instruct`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct) is its instruction-tuned variant.
-
-Instruction tuned models are trained to follow a specific conversational structure, making them more suitable for chatbot applications. Moreover, instruct models can handle complex interactions, including tool use, multimodal inputs, and function calling.
-
-To make a base model behave like an instruct model, we need to format our prompts in a consistent way that the model can understand. This is where chat templates come in. ChatML is one such template format that structures conversations with clear role indicators (system, user, assistant). Here's a guide on [ChatML](https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct/blob/e2c3f7557efbdec707ae3a336371d169783f1da1/tokenizer_config.json#L146).
-
-> [!WARNING]
-> When using an instruct model, always verify you're using the correct chat template format. Using the wrong template can result in poor model performance or unexpected behavior. The easiest way to ensure this is to check the model tokenizer configuration on the Hub. For example, the `SmolLM2-135M-Instruct` model uses this configuration.
-
-### Common Template Formats
-
-Before diving into specific implementations, it's important to understand how different models expect their conversations to be formatted. Let's explore some common template formats using a simple example conversation:
-
-We'll use the following conversation structure for all examples:
-
-```python
-messages = [
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "Hello!"},
- {"role": "assistant", "content": "Hi! How can I help you today?"},
- {"role": "user", "content": "What's the weather?"},
-]
-```
-
-This is the ChatML template used in models like SmolLM2 and Qwen 2:
-
-```sh
-<|im_start|>system
-You are a helpful assistant.<|im_end|>
-<|im_start|>user
-Hello!<|im_end|>
-<|im_start|>assistant
-Hi! How can I help you today?<|im_end|>
-<|im_start|>user
-What's the weather?<|im_start|>assistant
-```
-
-This is using the `mistral` template format:
-
-```sh
-[INST] You are a helpful assistant. [/INST]
-Hi! How can I help you today?
-[INST] Hello! [/INST]
-```
-
-Key differences between these formats include:
-1. **System Message Handling**:
- - Llama 2 wraps system messages in `<>` tags
- - Llama 3 uses `<|system|>` tags with `` endings
- - Mistral includes system message in the first instruction
- - Qwen uses explicit `system` role with `<|im_start|>` tags
- - ChatGPT uses `SYSTEM:` prefix
-
-2. **Message Boundaries**:
- - Llama 2 uses `[INST]` and `[/INST]` tags
- - Llama 3 uses role-specific tags (`<|system|>`, `<|user|>`, `<|assistant|>`) with `` endings
- - Mistral uses `[INST]` and `[/INST]` with `` and ``
- - Qwen uses role-specific start/end tokens
-
-3. **Special Tokens**:
- - Llama 2 uses `` and `` for conversation boundaries
- - Llama 3 uses `` to end each message
- - Mistral uses `` and `` for turn boundaries
- - Qwen uses role-specific start/end tokens
-
-Understanding these differences is key to working with various models. Let's look at how the transformers library helps us handle these variations automatically:
-
-```python
-from transformers import AutoTokenizer
-
-# These will use different templates automatically
-mistral_tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.1")
-qwen_tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B-Chat")
-smol_tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-135M-Instruct")
-
-messages = [
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "Hello!"},
-]
-
-# Each will format according to its model's template
-mistral_chat = mistral_tokenizer.apply_chat_template(messages, tokenize=False)
-qwen_chat = qwen_tokenizer.apply_chat_template(messages, tokenize=False)
-smol_chat = smol_tokenizer.apply_chat_template(messages, tokenize=False)
-```
-
-
-Click to see template examples
-
-Qwen 2 and SmolLM2 ChatML template:
-
-```sh
-<|im_start|>system
-You are a helpful assistant.<|im_end|>
-<|im_start|>user
-Hello!<|im_end|>
-<|im_start|>assistant
-Hi! How can I help you today?<|im_end|>
-<|im_start|>user
-What's the weather?<|im_start|>assistant
-```
-
-Mistral template:
-
-```sh
-[INST] You are a helpful assistant. [/INST]
-Hi! How can I help you today?
-[INST] Hello! [/INST]
-```
-
-
-
-
-### Advanced Features
-Chat templates can handle more complex scenarios beyond just conversational interactions, including:
-
-1. **Tool Use**: When models need to interact with external tools or APIs
-2. **Multimodal Inputs**: For handling images, audio, or other media types
-3. **Function Calling**: For structured function execution
-4. **Multi-turn Context**: For maintaining conversation history
-
-> [!TIP]
-> When implementing advanced features:
-> - Test thoroughly with your specific model. Vision and tool use template are particularly diverse.
-> - Monitor token usage carefully between each feature and model.
-> - Document the expected format for each feature
-
-For multimodal conversations, chat templates can include image references or base64-encoded images:
-
-```python
-messages = [
- {
- "role": "system",
- "content": "You are a helpful vision assistant that can analyze images.",
- },
- {
- "role": "user",
- "content": [
- {"type": "text", "text": "What's in this image?"},
- {"type": "image", "image_url": "https://example.com/image.jpg"},
- ],
- },
-]
-```
-
-Here's an example of a chat template with tool use:
-
-```python
-messages = [
- {
- "role": "system",
- "content": "You are an AI assistant that can use tools. Available tools: calculator, weather_api",
- },
- {"role": "user", "content": "What's 123 * 456 and is it raining in Paris?"},
- {
- "role": "assistant",
- "content": "Let me help you with that.",
- "tool_calls": [
- {
- "tool": "calculator",
- "parameters": {"operation": "multiply", "x": 123, "y": 456},
- },
- {"tool": "weather_api", "parameters": {"city": "Paris", "country": "France"}},
- ],
- },
- {"role": "tool", "tool_name": "calculator", "content": "56088"},
- {
- "role": "tool",
- "tool_name": "weather_api",
- "content": "{'condition': 'rain', 'temperature': 15}",
- },
-]
-```
-
-## Best Practices
-
-### General Guidelines
-When working with chat templates, follow these key practices:
-
-1. **Consistent Formatting**: Always use the same template format throughout your application
-2. **Clear Role Definition**: Clearly specify roles (system, user, assistant, tool) for each message
-3. **Context Management**: Be mindful of token limits when maintaining conversation history
-4. **Error Handling**: Include proper error handling for tool calls and multimodal inputs
-5. **Validation**: Validate message structure before sending to the model
-
-> [!WARNING]
-> Common pitfalls to avoid:
-> - Mixing different template formats in the same application
-> - Exceeding token limits with long conversation histories
-> - Not properly escaping special characters in messages
-> - Forgetting to validate input message structure
-> - Ignoring model-specific template requirements
-
-## Hands-on Exercise
-
-Let's practice implementing chat templates with a real-world example.
-
-> [!TIP]
-> Follow these steps to convert the `HuggingFaceTB/smoltalk` dataset into chatml format:
->
-> 1. Load the dataset:
-> ```python
-> from datasets import load_dataset
->
-> dataset = load_dataset("HuggingFaceTB/smoltalk")
-> ```
->
-> 2. Create a processing function:
-> ```python
-> def convert_to_chatml(example):
-> return {
-> "messages": [
-> {"role": "user", "content": example["input"]},
-> {"role": "assistant", "content": example["output"]},
-> ]
-> }
-> ```
->
-> 3. Apply the chat template using your chosen model's tokenizer
->
-> Remember to validate your output format matches your target model's requirements!
-
-## Additional Resources
-
-- [Hugging Face Chat Templating Guide](https://huggingface.co/docs/transformers/main/en/chat_templating)
-- [Transformers Documentation](https://huggingface.co/docs/transformers)
-- [Chat Templates Examples Repository](https://github.com/chujiezheng/chat_templates)
+
+
+# Chat Templates
+
+## Introduction
+
+Chat templates are essential for structuring interactions between language models and users. Whether you're building a simple chatbot or a complex AI agent, understanding how to properly format your conversations is crucial for getting the best results from your model. In this guide, we'll explore what chat templates are, why they matter, and how to use them effectively.
+
+> [!TIP]
+> Chat templates are crucial for:
+> - Maintaining consistent conversation structure
+> - Ensuring proper role identification
+> - Managing context across multiple turns
+> - Supporting advanced features like tool use
+
+## Model Types and Templates
+
+### Base Models vs Instruct Models
+A base model is trained on raw text data to predict the next token, while an instruct model is fine-tuned specifically to follow instructions and engage in conversations. For example, [`SmolLM2-135M`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M) is a base model, while [`SmolLM2-135M-Instruct`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct) is its instruction-tuned variant.
+
+Instruction tuned models are trained to follow a specific conversational structure, making them more suitable for chatbot applications. Moreover, instruct models can handle complex interactions, including tool use, multimodal inputs, and function calling.
+
+To make a base model behave like an instruct model, we need to format our prompts in a consistent way that the model can understand. This is where chat templates come in. ChatML is one such template format that structures conversations with clear role indicators (system, user, assistant). Here's a guide on [ChatML](https://huggingface.co/HuggingFaceTB/SmolLM2-135M-Instruct/blob/e2c3f7557efbdec707ae3a336371d169783f1da1/tokenizer_config.json#L146).
+
+> [!WARNING]
+> When using an instruct model, always verify you're using the correct chat template format. Using the wrong template can result in poor model performance or unexpected behavior. The easiest way to ensure this is to check the model tokenizer configuration on the Hub. For example, the `SmolLM2-135M-Instruct` model uses this configuration.
+
+### Common Template Formats
+
+Before diving into specific implementations, it's important to understand how different models expect their conversations to be formatted. Let's explore some common template formats using a simple example conversation:
+
+We'll use the following conversation structure for all examples:
+
+```python
+messages = [
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": "Hello!"},
+ {"role": "assistant", "content": "Hi! How can I help you today?"},
+ {"role": "user", "content": "What's the weather?"},
+]
+```
+
+This is the ChatML template used in models like SmolLM2 and Qwen 2:
+
+```sh
+<|im_start|>system
+You are a helpful assistant.<|im_end|>
+<|im_start|>user
+Hello!<|im_end|>
+<|im_start|>assistant
+Hi! How can I help you today?<|im_end|>
+<|im_start|>user
+What's the weather?<|im_start|>assistant
+```
+
+This is using the `mistral` template format:
+
+```sh
+[INST] You are a helpful assistant. [/INST]
+Hi! How can I help you today?
+[INST] Hello! [/INST]
+```
+
+Key differences between these formats include:
+1. **System Message Handling**:
+ - Llama 2 wraps system messages in `<>` tags
+ - Llama 3 uses `<|system|>` tags with `` endings
+ - Mistral includes system message in the first instruction
+ - Qwen uses explicit `system` role with `<|im_start|>` tags
+ - ChatGPT uses `SYSTEM:` prefix
+
+2. **Message Boundaries**:
+ - Llama 2 uses `[INST]` and `[/INST]` tags
+ - Llama 3 uses role-specific tags (`<|system|>`, `<|user|>`, `<|assistant|>`) with `` endings
+ - Mistral uses `[INST]` and `[/INST]` with `` and ``
+ - Qwen uses role-specific start/end tokens
+
+3. **Special Tokens**:
+ - Llama 2 uses `` and `` for conversation boundaries
+ - Llama 3 uses `` to end each message
+ - Mistral uses `` and `` for turn boundaries
+ - Qwen uses role-specific start/end tokens
+
+Understanding these differences is key to working with various models. Let's look at how the transformers library helps us handle these variations automatically:
+
+```python
+from transformers import AutoTokenizer
+
+# These will use different templates automatically
+mistral_tokenizer = AutoTokenizer.from_pretrained("mistralai/Mistral-7B-Instruct-v0.1")
+qwen_tokenizer = AutoTokenizer.from_pretrained("Qwen/Qwen-7B-Chat")
+smol_tokenizer = AutoTokenizer.from_pretrained("HuggingFaceTB/SmolLM2-135M-Instruct")
+
+messages = [
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": "Hello!"},
+]
+
+# Each will format according to its model's template
+mistral_chat = mistral_tokenizer.apply_chat_template(messages, tokenize=False)
+qwen_chat = qwen_tokenizer.apply_chat_template(messages, tokenize=False)
+smol_chat = smol_tokenizer.apply_chat_template(messages, tokenize=False)
+```
+
+
+Click to see template examples
+
+Qwen 2 and SmolLM2 ChatML template:
+
+```sh
+<|im_start|>system
+You are a helpful assistant.<|im_end|>
+<|im_start|>user
+Hello!<|im_end|>
+<|im_start|>assistant
+Hi! How can I help you today?<|im_end|>
+<|im_start|>user
+What's the weather?<|im_start|>assistant
+```
+
+Mistral template:
+
+```sh
+[INST] You are a helpful assistant. [/INST]
+Hi! How can I help you today?
+[INST] Hello! [/INST]
+```
+
+
+
+
+### Advanced Features
+Chat templates can handle more complex scenarios beyond just conversational interactions, including:
+
+1. **Tool Use**: When models need to interact with external tools or APIs
+2. **Multimodal Inputs**: For handling images, audio, or other media types
+3. **Function Calling**: For structured function execution
+4. **Multi-turn Context**: For maintaining conversation history
+
+> [!TIP]
+> When implementing advanced features:
+> - Test thoroughly with your specific model. Vision and tool use template are particularly diverse.
+> - Monitor token usage carefully between each feature and model.
+> - Document the expected format for each feature
+
+For multimodal conversations, chat templates can include image references or base64-encoded images:
+
+```python
+messages = [
+ {
+ "role": "system",
+ "content": "You are a helpful vision assistant that can analyze images.",
+ },
+ {
+ "role": "user",
+ "content": [
+ {"type": "text", "text": "What's in this image?"},
+ {"type": "image", "image_url": "https://example.com/image.jpg"},
+ ],
+ },
+]
+```
+
+Here's an example of a chat template with tool use:
+
+```python
+messages = [
+ {
+ "role": "system",
+ "content": "You are an AI assistant that can use tools. Available tools: calculator, weather_api",
+ },
+ {"role": "user", "content": "What's 123 * 456 and is it raining in Paris?"},
+ {
+ "role": "assistant",
+ "content": "Let me help you with that.",
+ "tool_calls": [
+ {
+ "tool": "calculator",
+ "parameters": {"operation": "multiply", "x": 123, "y": 456},
+ },
+ {"tool": "weather_api", "parameters": {"city": "Paris", "country": "France"}},
+ ],
+ },
+ {"role": "tool", "tool_name": "calculator", "content": "56088"},
+ {
+ "role": "tool",
+ "tool_name": "weather_api",
+ "content": "{'condition': 'rain', 'temperature': 15}",
+ },
+]
+```
+
+## Best Practices
+
+### General Guidelines
+When working with chat templates, follow these key practices:
+
+1. **Consistent Formatting**: Always use the same template format throughout your application
+2. **Clear Role Definition**: Clearly specify roles (system, user, assistant, tool) for each message
+3. **Context Management**: Be mindful of token limits when maintaining conversation history
+4. **Error Handling**: Include proper error handling for tool calls and multimodal inputs
+5. **Validation**: Validate message structure before sending to the model
+
+> [!WARNING]
+> Common pitfalls to avoid:
+> - Mixing different template formats in the same application
+> - Exceeding token limits with long conversation histories
+> - Not properly escaping special characters in messages
+> - Forgetting to validate input message structure
+> - Ignoring model-specific template requirements
+
+## Hands-on Exercise
+
+Let's practice implementing chat templates with a real-world example.
+
+> [!TIP]
+> Follow these steps to convert the `HuggingFaceTB/smoltalk` dataset into chatml format:
+>
+> 1. Load the dataset:
+> ```python
+> from datasets import load_dataset
+>
+> dataset = load_dataset("HuggingFaceTB/smoltalk")
+> ```
+>
+> 2. Create a processing function:
+> ```python
+> def convert_to_chatml(example):
+> return {
+> "messages": [
+> {"role": "user", "content": example["input"]},
+> {"role": "assistant", "content": example["output"]},
+> ]
+> }
+> ```
+>
+> 3. Apply the chat template using your chosen model's tokenizer
+>
+> Remember to validate your output format matches your target model's requirements!
+
+## Additional Resources
+
+- [Hugging Face Chat Templating Guide](https://huggingface.co/docs/transformers/main/en/chat_templating)
+- [Transformers Documentation](https://huggingface.co/docs/transformers)
+- [Chat Templates Examples Repository](https://github.com/chujiezheng/chat_templates)
diff --git a/chapters/en/chapter11/3.mdx b/chapters/en/chapter11/3.mdx
index 2967f7bbc..eda64dd26 100644
--- a/chapters/en/chapter11/3.mdx
+++ b/chapters/en/chapter11/3.mdx
@@ -1,378 +1,378 @@
-
-
-# Supervised Fine-Tuning
-
-Supervised Fine-Tuning (SFT) is a process primarily used to adapt pre-trained language models to follow instructions, engage in dialogue, and use specific output formats. While pre-trained models have impressive general capabilities, SFT helps transform them into assistant-like models that can better understand and respond to user prompts. This is typically done by training on datasets of human-written conversations and instructions.
-
-This page provides a step-by-step guide to fine-tuning the [`deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B`](https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B) model using the [`SFTTrainer`](https://huggingface.co/docs/trl/en/sft_trainer). By following these steps, you can adapt the model to perform specific tasks more effectively.
-
-## When to Use SFT
-
-Before diving into implementation, it's important to understand when SFT is the right choice for your project. As a first step, you should consider whether using an existing instruction-tuned model with well-crafted prompts would suffice for your use case. SFT involves significant computational resources and engineering effort, so it should only be pursued when prompting existing models proves insufficient.
-
-> [!TIP]
-> Consider SFT only if you:
-> - Need additional performance beyond what prompting can achieve
-> - Have a specific use case where the cost of using a large general-purpose model outweighs the cost of fine-tuning a smaller model
-> - Require specialized output formats or domain-specific knowledge that existing models struggle with
-
-If you determine that SFT is necessary, the decision to proceed depends on two primary factors:
-
-### Template Control
-SFT allows precise control over the model's output structure. This is particularly valuable when you need the model to:
-1. Generate responses in a specific chat template format
-2. Follow strict output schemas
-3. Maintain consistent styling across responses
-
-### Domain Adaptation
-When working in specialized domains, SFT helps align the model with domain-specific requirements by:
-1. Teaching domain terminology and concepts
-2. Enforcing professional standards
-3. Handling technical queries appropriately
-4. Following industry-specific guidelines
-
-> [!TIP]
-> Before starting SFT, evaluate whether your use case requires:
-> - Precise output formatting
-> - Domain-specific knowledge
-> - Consistent response patterns
-> - Adherence to specific guidelines
->
-> This evaluation will help determine if SFT is the right approach for your needs.
-
-## Dataset Preparation
-
-The supervised fine-tuning process requires a task-specific dataset structured with input-output pairs. Each pair should consist of:
-1. An input prompt
-2. The expected model response
-3. Any additional context or metadata
-
-The quality of your training data is crucial for successful fine-tuning. Let's look at how to prepare and validate your dataset:
-
-
-
-## Training Configuration
-
-The success of your fine-tuning depends heavily on choosing the right training parameters. Let's explore each important parameter and how to configure them effectively:
-
-The SFTTrainer configuration requires consideration of several parameters that control the training process. Let's explore each parameter and their purpose:
-
-1. **Training Duration Parameters**:
- - `num_train_epochs`: Controls total training duration
- - `max_steps`: Alternative to epochs, sets maximum number of training steps
- - More epochs allow better learning but risk overfitting
-
-2. **Batch Size Parameters**:
- - `per_device_train_batch_size`: Determines memory usage and training stability
- - `gradient_accumulation_steps`: Enables larger effective batch sizes
- - Larger batches provide more stable gradients but require more memory
-
-3. **Learning Rate Parameters**:
- - `learning_rate`: Controls size of weight updates
- - `warmup_ratio`: Portion of training used for learning rate warmup
- - Too high can cause instability, too low results in slow learning
-
-4. **Monitoring Parameters**:
- - `logging_steps`: Frequency of metric logging
- - `eval_steps`: How often to evaluate on validation data
- - `save_steps`: Frequency of model checkpoint saves
-
-> [!TIP]
-> Start with conservative values and adjust based on monitoring:
-> - Begin with 1-3 epochs
-> - Use smaller batch sizes initially
-> - Monitor validation metrics closely
-> - Adjust learning rate if training is unstable
-
-## Implementation with TRL
-
-Now that we understand the key components, let's implement the training with proper validation and monitoring. We will use the `SFTTrainer` class from the Transformers Reinforcement Learning (TRL) library, which is built on top of the `transformers` library. Here's a complete example using the TRL library:
-
-```python
-from datasets import load_dataset
-from trl import SFTConfig, SFTTrainer
-import torch
-
-# Set device
-device = "cuda" if torch.cuda.is_available() else "cpu"
-
-# Load dataset
-dataset = load_dataset("HuggingFaceTB/smoltalk", "all")
-
-# Configure model and tokenizer
-model_name = "HuggingFaceTB/SmolLM2-135M"
-model = AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path=model_name).to(
- device
-)
-tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name_or_path=model_name)
-# Setup chat template
-model, tokenizer = setup_chat_format(model=model, tokenizer=tokenizer)
-
-# Configure trainer
-training_args = SFTConfig(
- output_dir="./sft_output",
- max_steps=1000,
- per_device_train_batch_size=4,
- learning_rate=5e-5,
- logging_steps=10,
- save_steps=100,
- eval_strategy="steps",
- eval_steps=50,
-)
-
-# Initialize trainer
-trainer = SFTTrainer(
- model=model,
- args=training_args,
- train_dataset=dataset["train"],
- eval_dataset=dataset["test"],
- processing_class=tokenizer,
-)
-
-# Start training
-trainer.train()
-```
-
-> [!TIP]
-> When using a dataset with a "messages" field (like the example above), the SFTTrainer automatically applies the model's chat template, which it retrieves from the hub. This means you don't need any additional configuration to handle chat-style conversations - the trainer will format the messages according to the model's expected template format.
-
-## Packing the Dataset
-
-The SFTTrainer supports example packing to optimize training efficiency. This feature allows multiple short examples to be packed into the same input sequence, maximizing GPU utilization during training. To enable packing, simply set `packing=True` in the SFTConfig constructor. When using packed datasets with `max_steps`, be aware that you may train for more epochs than expected depending on your packing configuration. You can customize how examples are combined using a formatting function - particularly useful when working with datasets that have multiple fields like question-answer pairs. For evaluation datasets, you can disable packing by setting `eval_packing=False` in the SFTConfig. Here's a basic example of customizing the packing configuration:
-
-```python
-# Configure packing
-training_args = SFTConfig(packing=True)
-
-trainer = SFTTrainer(model=model, train_dataset=dataset, args=training_args)
-
-trainer.train()
-```
-
-When packing the dataset with multiple fields, you can define a custom formatting function to combine the fields into a single input sequence. This function should take a list of examples and return a dictionary with the packed input sequence. Here's an example of a custom formatting function:
-
-```python
-def formatting_func(example):
- text = f"### Question: {example['question']}\n ### Answer: {example['answer']}"
- return text
-
-
-training_args = SFTConfig(packing=True)
-trainer = SFTTrainer(
- "facebook/opt-350m",
- train_dataset=dataset,
- args=training_args,
- formatting_func=formatting_func,
-)
-```
-
-## Monitoring Training Progress
-
-Effective monitoring is crucial for successful fine-tuning. Let's explore what to watch for during training:
-
-### Understanding Loss Patterns
-
-Training loss typically follows three distinct phases:
-1. Initial Sharp Drop: Rapid adaptation to new data distribution
-2. Gradual Stabilization: Learning rate slows as model fine-tunes
-3. Convergence: Loss values stabilize, indicating training completion
-
-
-
-### Metrics to Monitor
-
-Effective monitoring involves tracking quantitative metrics, and evaluating qualitative metrics. Available metrics are:
-
-- Training loss
-- Validation loss
-- Learning rate progression
-- Gradient norms
-
-> [!WARNING]
-> Watch for these warning signs during training:
-> 1. Validation loss increasing while training loss decreases (overfitting)
-> 2. No significant improvement in loss values (underfitting)
-> 3. Extremely low loss values (potential memorization)
-> 4. Inconsistent output formatting (template learning issues)
-
-### The Path to Convergence
-
-As training progresses, the loss curve should gradually stabilize. The key indicator of healthy training is a small gap between training and validation loss, suggesting
-the model is learning generalizable patterns rather than memorizing specific examples. The absolute loss values will vary depending on your task and dataset.
-
-### Monitoring Training Progress
-
-The graph above shows a typical training progression. Notice how both training and validation loss decrease sharply at first, then gradually level off. This pattern indicates the model is learning effectively while maintaining generalization ability.
-
-### Warning Signs to Watch For
-
-Several patterns in the loss curves can indicate potential issues. Below we illustrate common warning signs and solutions that we can consider.
-
-
-
-If the validation loss decreases at a significantly slower rate than training loss, your model is likely overfitting to the training data. Consider:
-- Reducing the training steps
-- Increasing the dataset size
-- Validating dataset quality and diversity
-
-
-
-If the loss doesn't show significant improvement, the model might be:
-- Learning too slowly (try increasing the learning rate)
-- Struggling with the task (check data quality and task complexity)
-- Hitting architecture limitations (consider a different model)
-
-
-
-Extremely low loss values could suggest memorization rather than learning. This is particularly concerning if:
-- The model performs poorly on new, similar examples
-- The outputs lack diversity
-- The responses are too similar to training examples
-
-> [!WARNING]
-> Monitor both the loss values and the model's actual outputs during training. Sometimes the loss can look good while the model develops unwanted behaviors. Regular qualitative evaluation of the model's responses helps catch issues that metrics alone might miss.
-
-We should note that the interpretation of the loss values we outline here is aimed on the most common case, and in fact, loss values can behave on various ways depending on the model, the dataset, the training parameters, etc. If you interested in exploring more about outlined patterns, you should check out this blog post by the people at [Fast AI](https://www.fast.ai/posts/2023-09-04-learning-jumps/).
-
-## Evaluation after SFT
-
-In section [11.4](/en/chapter11/4) we will learn how to evaluate the model using benchmark datasets. For now, we will focus on the qualitative evaluation of the model.
-
-After completing SFT, consider these follow-up actions:
-
-1. Evaluate the model thoroughly on held-out test data
-2. Validate template adherence across various inputs
-3. Test domain-specific knowledge retention
-4. Monitor real-world performance metrics
-
-> [!TIP]
-> Document your training process, including:
-> - Dataset characteristics
-> - Training parameters
-> - Performance metrics
-> - Known limitations
-> This documentation will be valuable for future model iterations.
-
-## Quiz
-
-### 1. What parameters control the training duration in SFT?
-
-
-
-### 2. Which pattern in the loss curves indicates potential overfitting?
-
-
-
-### 3. What is gradient_accumulation_steps used for?
-
-
-
-### 4. What should you monitor during SFT training?
-
-
-
-### 5. What indicates healthy convergence during training?
-
-
-
-## 💐 Nice work!
-
-You've learned how to fine-tune models using SFT! To continue your learning:
-1. Try the notebook with different parameters
-2. Experiment with other datasets
-3. Contribute improvements to the course material
-
-## Additional Resources
-
-- [TRL Documentation](https://huggingface.co/docs/trl)
-- [SFT Examples Repository](https://github.com/huggingface/trl/blob/main/trl/scripts/sft.py)
-- [Fine-tuning Best Practices](https://huggingface.co/docs/transformers/training)
+
+
+# Supervised Fine-Tuning
+
+Supervised Fine-Tuning (SFT) is a process primarily used to adapt pre-trained language models to follow instructions, engage in dialogue, and use specific output formats. While pre-trained models have impressive general capabilities, SFT helps transform them into assistant-like models that can better understand and respond to user prompts. This is typically done by training on datasets of human-written conversations and instructions.
+
+This page provides a step-by-step guide to fine-tuning the [`deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B`](https://huggingface.co/deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B) model using the [`SFTTrainer`](https://huggingface.co/docs/trl/en/sft_trainer). By following these steps, you can adapt the model to perform specific tasks more effectively.
+
+## When to Use SFT
+
+Before diving into implementation, it's important to understand when SFT is the right choice for your project. As a first step, you should consider whether using an existing instruction-tuned model with well-crafted prompts would suffice for your use case. SFT involves significant computational resources and engineering effort, so it should only be pursued when prompting existing models proves insufficient.
+
+> [!TIP]
+> Consider SFT only if you:
+> - Need additional performance beyond what prompting can achieve
+> - Have a specific use case where the cost of using a large general-purpose model outweighs the cost of fine-tuning a smaller model
+> - Require specialized output formats or domain-specific knowledge that existing models struggle with
+
+If you determine that SFT is necessary, the decision to proceed depends on two primary factors:
+
+### Template Control
+SFT allows precise control over the model's output structure. This is particularly valuable when you need the model to:
+1. Generate responses in a specific chat template format
+2. Follow strict output schemas
+3. Maintain consistent styling across responses
+
+### Domain Adaptation
+When working in specialized domains, SFT helps align the model with domain-specific requirements by:
+1. Teaching domain terminology and concepts
+2. Enforcing professional standards
+3. Handling technical queries appropriately
+4. Following industry-specific guidelines
+
+> [!TIP]
+> Before starting SFT, evaluate whether your use case requires:
+> - Precise output formatting
+> - Domain-specific knowledge
+> - Consistent response patterns
+> - Adherence to specific guidelines
+>
+> This evaluation will help determine if SFT is the right approach for your needs.
+
+## Dataset Preparation
+
+The supervised fine-tuning process requires a task-specific dataset structured with input-output pairs. Each pair should consist of:
+1. An input prompt
+2. The expected model response
+3. Any additional context or metadata
+
+The quality of your training data is crucial for successful fine-tuning. Let's look at how to prepare and validate your dataset:
+
+
+
+## Training Configuration
+
+The success of your fine-tuning depends heavily on choosing the right training parameters. Let's explore each important parameter and how to configure them effectively:
+
+The SFTTrainer configuration requires consideration of several parameters that control the training process. Let's explore each parameter and their purpose:
+
+1. **Training Duration Parameters**:
+ - `num_train_epochs`: Controls total training duration
+ - `max_steps`: Alternative to epochs, sets maximum number of training steps
+ - More epochs allow better learning but risk overfitting
+
+2. **Batch Size Parameters**:
+ - `per_device_train_batch_size`: Determines memory usage and training stability
+ - `gradient_accumulation_steps`: Enables larger effective batch sizes
+ - Larger batches provide more stable gradients but require more memory
+
+3. **Learning Rate Parameters**:
+ - `learning_rate`: Controls size of weight updates
+ - `warmup_ratio`: Portion of training used for learning rate warmup
+ - Too high can cause instability, too low results in slow learning
+
+4. **Monitoring Parameters**:
+ - `logging_steps`: Frequency of metric logging
+ - `eval_steps`: How often to evaluate on validation data
+ - `save_steps`: Frequency of model checkpoint saves
+
+> [!TIP]
+> Start with conservative values and adjust based on monitoring:
+> - Begin with 1-3 epochs
+> - Use smaller batch sizes initially
+> - Monitor validation metrics closely
+> - Adjust learning rate if training is unstable
+
+## Implementation with TRL
+
+Now that we understand the key components, let's implement the training with proper validation and monitoring. We will use the `SFTTrainer` class from the Transformers Reinforcement Learning (TRL) library, which is built on top of the `transformers` library. Here's a complete example using the TRL library:
+
+```python
+from datasets import load_dataset
+from trl import SFTConfig, SFTTrainer
+import torch
+
+# Set device
+device = "cuda" if torch.cuda.is_available() else "cpu"
+
+# Load dataset
+dataset = load_dataset("HuggingFaceTB/smoltalk", "all")
+
+# Configure model and tokenizer
+model_name = "HuggingFaceTB/SmolLM2-135M"
+model = AutoModelForCausalLM.from_pretrained(pretrained_model_name_or_path=model_name).to(
+ device
+)
+tokenizer = AutoTokenizer.from_pretrained(pretrained_model_name_or_path=model_name)
+# Setup chat template
+model, tokenizer = setup_chat_format(model=model, tokenizer=tokenizer)
+
+# Configure trainer
+training_args = SFTConfig(
+ output_dir="./sft_output",
+ max_steps=1000,
+ per_device_train_batch_size=4,
+ learning_rate=5e-5,
+ logging_steps=10,
+ save_steps=100,
+ eval_strategy="steps",
+ eval_steps=50,
+)
+
+# Initialize trainer
+trainer = SFTTrainer(
+ model=model,
+ args=training_args,
+ train_dataset=dataset["train"],
+ eval_dataset=dataset["test"],
+ processing_class=tokenizer,
+)
+
+# Start training
+trainer.train()
+```
+
+> [!TIP]
+> When using a dataset with a "messages" field (like the example above), the SFTTrainer automatically applies the model's chat template, which it retrieves from the hub. This means you don't need any additional configuration to handle chat-style conversations - the trainer will format the messages according to the model's expected template format.
+
+## Packing the Dataset
+
+The SFTTrainer supports example packing to optimize training efficiency. This feature allows multiple short examples to be packed into the same input sequence, maximizing GPU utilization during training. To enable packing, simply set `packing=True` in the SFTConfig constructor. When using packed datasets with `max_steps`, be aware that you may train for more epochs than expected depending on your packing configuration. You can customize how examples are combined using a formatting function - particularly useful when working with datasets that have multiple fields like question-answer pairs. For evaluation datasets, you can disable packing by setting `eval_packing=False` in the SFTConfig. Here's a basic example of customizing the packing configuration:
+
+```python
+# Configure packing
+training_args = SFTConfig(packing=True)
+
+trainer = SFTTrainer(model=model, train_dataset=dataset, args=training_args)
+
+trainer.train()
+```
+
+When packing the dataset with multiple fields, you can define a custom formatting function to combine the fields into a single input sequence. This function should take a list of examples and return a dictionary with the packed input sequence. Here's an example of a custom formatting function:
+
+```python
+def formatting_func(example):
+ text = f"### Question: {example['question']}\n ### Answer: {example['answer']}"
+ return text
+
+
+training_args = SFTConfig(packing=True)
+trainer = SFTTrainer(
+ "facebook/opt-350m",
+ train_dataset=dataset,
+ args=training_args,
+ formatting_func=formatting_func,
+)
+```
+
+## Monitoring Training Progress
+
+Effective monitoring is crucial for successful fine-tuning. Let's explore what to watch for during training:
+
+### Understanding Loss Patterns
+
+Training loss typically follows three distinct phases:
+1. Initial Sharp Drop: Rapid adaptation to new data distribution
+2. Gradual Stabilization: Learning rate slows as model fine-tunes
+3. Convergence: Loss values stabilize, indicating training completion
+
+
+
+### Metrics to Monitor
+
+Effective monitoring involves tracking quantitative metrics, and evaluating qualitative metrics. Available metrics are:
+
+- Training loss
+- Validation loss
+- Learning rate progression
+- Gradient norms
+
+> [!WARNING]
+> Watch for these warning signs during training:
+> 1. Validation loss increasing while training loss decreases (overfitting)
+> 2. No significant improvement in loss values (underfitting)
+> 3. Extremely low loss values (potential memorization)
+> 4. Inconsistent output formatting (template learning issues)
+
+### The Path to Convergence
+
+As training progresses, the loss curve should gradually stabilize. The key indicator of healthy training is a small gap between training and validation loss, suggesting
+the model is learning generalizable patterns rather than memorizing specific examples. The absolute loss values will vary depending on your task and dataset.
+
+### Monitoring Training Progress
+
+The graph above shows a typical training progression. Notice how both training and validation loss decrease sharply at first, then gradually level off. This pattern indicates the model is learning effectively while maintaining generalization ability.
+
+### Warning Signs to Watch For
+
+Several patterns in the loss curves can indicate potential issues. Below we illustrate common warning signs and solutions that we can consider.
+
+
+
+If the validation loss decreases at a significantly slower rate than training loss, your model is likely overfitting to the training data. Consider:
+- Reducing the training steps
+- Increasing the dataset size
+- Validating dataset quality and diversity
+
+
+
+If the loss doesn't show significant improvement, the model might be:
+- Learning too slowly (try increasing the learning rate)
+- Struggling with the task (check data quality and task complexity)
+- Hitting architecture limitations (consider a different model)
+
+
+
+Extremely low loss values could suggest memorization rather than learning. This is particularly concerning if:
+- The model performs poorly on new, similar examples
+- The outputs lack diversity
+- The responses are too similar to training examples
+
+> [!WARNING]
+> Monitor both the loss values and the model's actual outputs during training. Sometimes the loss can look good while the model develops unwanted behaviors. Regular qualitative evaluation of the model's responses helps catch issues that metrics alone might miss.
+
+We should note that the interpretation of the loss values we outline here is aimed on the most common case, and in fact, loss values can behave on various ways depending on the model, the dataset, the training parameters, etc. If you interested in exploring more about outlined patterns, you should check out this blog post by the people at [Fast AI](https://www.fast.ai/posts/2023-09-04-learning-jumps/).
+
+## Evaluation after SFT
+
+In section [11.4](/en/chapter11/4) we will learn how to evaluate the model using benchmark datasets. For now, we will focus on the qualitative evaluation of the model.
+
+After completing SFT, consider these follow-up actions:
+
+1. Evaluate the model thoroughly on held-out test data
+2. Validate template adherence across various inputs
+3. Test domain-specific knowledge retention
+4. Monitor real-world performance metrics
+
+> [!TIP]
+> Document your training process, including:
+> - Dataset characteristics
+> - Training parameters
+> - Performance metrics
+> - Known limitations
+> This documentation will be valuable for future model iterations.
+
+## Quiz
+
+### 1. What parameters control the training duration in SFT?
+
+
+
+### 2. Which pattern in the loss curves indicates potential overfitting?
+
+
+
+### 3. What is gradient_accumulation_steps used for?
+
+
+
+### 4. What should you monitor during SFT training?
+
+
+
+### 5. What indicates healthy convergence during training?
+
+
+
+## 💐 Nice work!
+
+You've learned how to fine-tune models using SFT! To continue your learning:
+1. Try the notebook with different parameters
+2. Experiment with other datasets
+3. Contribute improvements to the course material
+
+## Additional Resources
+
+- [TRL Documentation](https://huggingface.co/docs/trl)
+- [SFT Examples Repository](https://github.com/huggingface/trl/blob/main/trl/scripts/sft.py)
+- [Fine-tuning Best Practices](https://huggingface.co/docs/transformers/training)
diff --git a/chapters/en/chapter11/4.mdx b/chapters/en/chapter11/4.mdx
index 838cfff75..3625e23dd 100644
--- a/chapters/en/chapter11/4.mdx
+++ b/chapters/en/chapter11/4.mdx
@@ -1,164 +1,164 @@
-
-
-# LoRA (Low-Rank Adaptation)
-
-Fine-tuning large language models is a resource intensive process. LoRA is a technique that allows us to fine-tune large language models with a small number of parameters. It works by adding and optimizing smaller matrices to the attention weights, typically reducing trainable parameters by about 90%.
-
-## Understanding LoRA
-
-LoRA (Low-Rank Adaptation) is a parameter-efficient fine-tuning technique that freezes the pre-trained model weights and injects trainable rank decomposition matrices into the model's layers. Instead of training all model parameters during fine-tuning, LoRA decomposes the weight updates into smaller matrices through low-rank decomposition, significantly reducing the number of trainable parameters while maintaining model performance. For example, when applied to GPT-3 175B, LoRA reduced trainable parameters by 10,000x and GPU memory requirements by 3x compared to full fine-tuning. You can read more about LoRA in the [LoRA paper](https://arxiv.org/pdf/2106.09685).
-
-LoRA works by adding pairs of rank decomposition matrices to transformer layers, typically focusing on attention weights. During inference, these adapter weights can be merged with the base model, resulting in no additional latency overhead. LoRA is particularly useful for adapting large language models to specific tasks or domains while keeping resource requirements manageable.
-
-## Key advantages of LoRA
-
-1. **Memory Efficiency**:
- - Only adapter parameters are stored in GPU memory
- - Base model weights remain frozen and can be loaded in lower precision
- - Enables fine-tuning of large models on consumer GPUs
-
-2. **Training Features**:
- - Native PEFT/LoRA integration with minimal setup
- - Support for QLoRA (Quantized LoRA) for even better memory efficiency
-
-3. **Adapter Management**:
- - Adapter weight saving during checkpoints
- - Features to merge adapters back into base model
-
-## Loading LoRA Adapters with PEFT
-
-[PEFT](https://github.com/huggingface/peft) is a library that provides a unified interface for loading and managing PEFT methods, including LoRA. It allows you to easily load and switch between different PEFT methods, making it easier to experiment with different fine-tuning techniques.
-
-Adapters can be loaded onto a pretrained model with `load_adapter()`, which is useful for trying out different adapters whose weights aren't merged. Set the active adapter weights with the `set_adapter()` function. To return the base model, you could use unload() to unload all of the LoRA modules. This makes it easy to switch between different task-specific weights.
-
-```python
-from peft import PeftModel, PeftConfig
-
-config = PeftConfig.from_pretrained("ybelkada/opt-350m-lora")
-model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)
-lora_model = PeftModel.from_pretrained(model, "ybelkada/opt-350m-lora")
-```
-
-
-
-## Fine-tune LLM using `trl` and the `SFTTrainer` with LoRA
-
-The [SFTTrainer](https://huggingface.co/docs/trl/sft_trainer) from `trl` provides integration with LoRA adapters through the [PEFT](https://huggingface.co/docs/peft/en/index) library. This means that we can fine-tune a model in the same way as we did with SFT, but use LoRA to reduce the number of parameters we need to train.
-
-We'll use the `LoRAConfig` class from PEFT in our example. The setup requires just a few configuration steps:
-
-1. Define the LoRA configuration (rank, alpha, dropout)
-2. Create the SFTTrainer with PEFT config
-3. Train and save the adapter weights
-
-## LoRA Configuration
-
-Let's walk through the LoRA configuration and key parameters.
-
-| Parameter | Description |
-|-----------|-------------|
-| `r` (rank) | Dimension of the low-rank matrices used for weight updates. Typically between 4-32. Lower values provide more compression but potentially less expressiveness. |
-| `lora_alpha` | Scaling factor for LoRA layers, usually set to 2x the rank value. Higher values result in stronger adaptation effects. |
-| `lora_dropout` | Dropout probability for LoRA layers, typically 0.05-0.1. Higher values help prevent overfitting during training. |
-| `bias` | Controls training of bias terms. Options are "none", "all", or "lora_only". "none" is most common for memory efficiency. |
-| `target_modules` | Specifies which model modules to apply LoRA to. Can be "all-linear" or specific modules like "q_proj,v_proj". More modules enable greater adaptability but increase memory usage. |
-
-> [!TIP]
-> When implementing PEFT methods, start with small rank values (4-8) for LoRA and monitor training loss. Use validation sets to prevent overfitting and compare results with full fine-tuning baselines when possible. The effectiveness of different methods can vary by task, so experimentation is key.
-
-## Using TRL with PEFT
-
-PEFT methods can be combined with TRL for fine-tuning to reduce memory requirements. We can pass the `LoraConfig` to the model when loading it.
-
-```python
-from peft import LoraConfig
-
-# r: rank dimension for LoRA update matrices (smaller = more compression)
-rank_dimension = 6
-# lora_alpha: scaling factor for LoRA layers (higher = stronger adaptation)
-lora_alpha = 8
-# lora_dropout: dropout probability for LoRA layers (helps prevent overfitting)
-lora_dropout = 0.05
-
-peft_config = LoraConfig(
- r=rank_dimension, # Rank dimension - typically between 4-32
- lora_alpha=lora_alpha, # LoRA scaling factor - typically 2x rank
- lora_dropout=lora_dropout, # Dropout probability for LoRA layers
- bias="none", # Bias type for LoRA. the corresponding biases will be updated during training.
- target_modules="all-linear", # Which modules to apply LoRA to
- task_type="CAUSAL_LM", # Task type for model architecture
-)
-```
-
-Above, we used `device_map="auto"` to automatically assign the model to the correct device. You can also manually assign the model to a specific device using `device_map={"": device_index}`.
-
-We will also need to define the `SFTTrainer` with the LoRA configuration.
-
-```python
-# Create SFTTrainer with LoRA configuration
-trainer = SFTTrainer(
- model=model,
- args=args,
- train_dataset=dataset["train"],
- peft_config=peft_config, # LoRA configuration
- max_seq_length=max_seq_length, # Maximum sequence length
- processing_class=tokenizer,
-)
-```
-
-> [!TIP]
-> ✏️ **Try it out!** Build on your fine-tuned model from the previous section, but fine-tune it with LoRA. Use the `HuggingFaceTB/smoltalk` dataset to fine-tune a `deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B` model, using the LoRA configuration we defined above.
-
-## Merging LoRA Adapters
-
-After training with LoRA, you might want to merge the adapter weights back into the base model for easier deployment. This creates a single model with the combined weights, eliminating the need to load adapters separately during inference.
-
-The merging process requires attention to memory management and precision. Since you'll need to load both the base model and adapter weights simultaneously, ensure sufficient GPU/CPU memory is available. Using `device_map="auto"` in `transformers` will find the correct device for the model based on your hardware.
-
-Maintain consistent precision (e.g., float16) throughout the process, matching the precision used during training and saving the merged model in the same format for deployment.
-
-## Merging Implementation
-
-After training a LoRA adapter, you can merge the adapter weights back into the base model. Here's how to do it:
-
-```python
-import torch
-from transformers import AutoModelForCausalLM
-from peft import PeftModel
-
-# 1. Load the base model
-base_model = AutoModelForCausalLM.from_pretrained(
- "base_model_name", torch_dtype=torch.float16, device_map="auto"
-)
-
-# 2. Load the PEFT model with adapter
-peft_model = PeftModel.from_pretrained(
- base_model, "path/to/adapter", torch_dtype=torch.float16
-)
-
-# 3. Merge adapter weights with base model
-merged_model = peft_model.merge_and_unload()
-```
-
-If you encounter size discrepancies in the saved model, ensure you're also saving the tokenizer:
-
-```python
-# Save both model and tokenizer
-tokenizer = AutoTokenizer.from_pretrained("base_model_name")
-merged_model.save_pretrained("path/to/save/merged_model")
-tokenizer.save_pretrained("path/to/save/merged_model")
-```
-
-> [!TIP]
-> ✏️ **Try it out!** Merge the adapter weights back into the base model. Use the `HuggingFaceTB/smoltalk` dataset to fine-tune a `deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B` model, using the LoRA configuration we defined above.
-
-
-# Resources
-
-- [LoRA: Low-Rank Adaptation of Large Language Models](https://arxiv.org/pdf/2106.09685)
-- [PEFT Documentation](https://huggingface.co/docs/peft)
-- [Hugging Face blog post on PEFT](https://huggingface.co/blog/peft)
+
+
+# LoRA (Low-Rank Adaptation)
+
+Fine-tuning large language models is a resource intensive process. LoRA is a technique that allows us to fine-tune large language models with a small number of parameters. It works by adding and optimizing smaller matrices to the attention weights, typically reducing trainable parameters by about 90%.
+
+## Understanding LoRA
+
+LoRA (Low-Rank Adaptation) is a parameter-efficient fine-tuning technique that freezes the pre-trained model weights and injects trainable rank decomposition matrices into the model's layers. Instead of training all model parameters during fine-tuning, LoRA decomposes the weight updates into smaller matrices through low-rank decomposition, significantly reducing the number of trainable parameters while maintaining model performance. For example, when applied to GPT-3 175B, LoRA reduced trainable parameters by 10,000x and GPU memory requirements by 3x compared to full fine-tuning. You can read more about LoRA in the [LoRA paper](https://arxiv.org/pdf/2106.09685).
+
+LoRA works by adding pairs of rank decomposition matrices to transformer layers, typically focusing on attention weights. During inference, these adapter weights can be merged with the base model, resulting in no additional latency overhead. LoRA is particularly useful for adapting large language models to specific tasks or domains while keeping resource requirements manageable.
+
+## Key advantages of LoRA
+
+1. **Memory Efficiency**:
+ - Only adapter parameters are stored in GPU memory
+ - Base model weights remain frozen and can be loaded in lower precision
+ - Enables fine-tuning of large models on consumer GPUs
+
+2. **Training Features**:
+ - Native PEFT/LoRA integration with minimal setup
+ - Support for QLoRA (Quantized LoRA) for even better memory efficiency
+
+3. **Adapter Management**:
+ - Adapter weight saving during checkpoints
+ - Features to merge adapters back into base model
+
+## Loading LoRA Adapters with PEFT
+
+[PEFT](https://github.com/huggingface/peft) is a library that provides a unified interface for loading and managing PEFT methods, including LoRA. It allows you to easily load and switch between different PEFT methods, making it easier to experiment with different fine-tuning techniques.
+
+Adapters can be loaded onto a pretrained model with `load_adapter()`, which is useful for trying out different adapters whose weights aren't merged. Set the active adapter weights with the `set_adapter()` function. To return the base model, you could use unload() to unload all of the LoRA modules. This makes it easy to switch between different task-specific weights.
+
+```python
+from peft import PeftModel, PeftConfig
+
+config = PeftConfig.from_pretrained("ybelkada/opt-350m-lora")
+model = AutoModelForCausalLM.from_pretrained(config.base_model_name_or_path)
+lora_model = PeftModel.from_pretrained(model, "ybelkada/opt-350m-lora")
+```
+
+
+
+## Fine-tune LLM using `trl` and the `SFTTrainer` with LoRA
+
+The [SFTTrainer](https://huggingface.co/docs/trl/sft_trainer) from `trl` provides integration with LoRA adapters through the [PEFT](https://huggingface.co/docs/peft/en/index) library. This means that we can fine-tune a model in the same way as we did with SFT, but use LoRA to reduce the number of parameters we need to train.
+
+We'll use the `LoRAConfig` class from PEFT in our example. The setup requires just a few configuration steps:
+
+1. Define the LoRA configuration (rank, alpha, dropout)
+2. Create the SFTTrainer with PEFT config
+3. Train and save the adapter weights
+
+## LoRA Configuration
+
+Let's walk through the LoRA configuration and key parameters.
+
+| Parameter | Description |
+|-----------|-------------|
+| `r` (rank) | Dimension of the low-rank matrices used for weight updates. Typically between 4-32. Lower values provide more compression but potentially less expressiveness. |
+| `lora_alpha` | Scaling factor for LoRA layers, usually set to 2x the rank value. Higher values result in stronger adaptation effects. |
+| `lora_dropout` | Dropout probability for LoRA layers, typically 0.05-0.1. Higher values help prevent overfitting during training. |
+| `bias` | Controls training of bias terms. Options are "none", "all", or "lora_only". "none" is most common for memory efficiency. |
+| `target_modules` | Specifies which model modules to apply LoRA to. Can be "all-linear" or specific modules like "q_proj,v_proj". More modules enable greater adaptability but increase memory usage. |
+
+> [!TIP]
+> When implementing PEFT methods, start with small rank values (4-8) for LoRA and monitor training loss. Use validation sets to prevent overfitting and compare results with full fine-tuning baselines when possible. The effectiveness of different methods can vary by task, so experimentation is key.
+
+## Using TRL with PEFT
+
+PEFT methods can be combined with TRL for fine-tuning to reduce memory requirements. We can pass the `LoraConfig` to the model when loading it.
+
+```python
+from peft import LoraConfig
+
+# r: rank dimension for LoRA update matrices (smaller = more compression)
+rank_dimension = 6
+# lora_alpha: scaling factor for LoRA layers (higher = stronger adaptation)
+lora_alpha = 8
+# lora_dropout: dropout probability for LoRA layers (helps prevent overfitting)
+lora_dropout = 0.05
+
+peft_config = LoraConfig(
+ r=rank_dimension, # Rank dimension - typically between 4-32
+ lora_alpha=lora_alpha, # LoRA scaling factor - typically 2x rank
+ lora_dropout=lora_dropout, # Dropout probability for LoRA layers
+ bias="none", # Bias type for LoRA. the corresponding biases will be updated during training.
+ target_modules="all-linear", # Which modules to apply LoRA to
+ task_type="CAUSAL_LM", # Task type for model architecture
+)
+```
+
+Above, we used `device_map="auto"` to automatically assign the model to the correct device. You can also manually assign the model to a specific device using `device_map={"": device_index}`.
+
+We will also need to define the `SFTTrainer` with the LoRA configuration.
+
+```python
+# Create SFTTrainer with LoRA configuration
+trainer = SFTTrainer(
+ model=model,
+ args=args,
+ train_dataset=dataset["train"],
+ peft_config=peft_config, # LoRA configuration
+ max_seq_length=max_seq_length, # Maximum sequence length
+ processing_class=tokenizer,
+)
+```
+
+> [!TIP]
+> ✏️ **Try it out!** Build on your fine-tuned model from the previous section, but fine-tune it with LoRA. Use the `HuggingFaceTB/smoltalk` dataset to fine-tune a `deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B` model, using the LoRA configuration we defined above.
+
+## Merging LoRA Adapters
+
+After training with LoRA, you might want to merge the adapter weights back into the base model for easier deployment. This creates a single model with the combined weights, eliminating the need to load adapters separately during inference.
+
+The merging process requires attention to memory management and precision. Since you'll need to load both the base model and adapter weights simultaneously, ensure sufficient GPU/CPU memory is available. Using `device_map="auto"` in `transformers` will find the correct device for the model based on your hardware.
+
+Maintain consistent precision (e.g., float16) throughout the process, matching the precision used during training and saving the merged model in the same format for deployment.
+
+## Merging Implementation
+
+After training a LoRA adapter, you can merge the adapter weights back into the base model. Here's how to do it:
+
+```python
+import torch
+from transformers import AutoModelForCausalLM
+from peft import PeftModel
+
+# 1. Load the base model
+base_model = AutoModelForCausalLM.from_pretrained(
+ "base_model_name", torch_dtype=torch.float16, device_map="auto"
+)
+
+# 2. Load the PEFT model with adapter
+peft_model = PeftModel.from_pretrained(
+ base_model, "path/to/adapter", torch_dtype=torch.float16
+)
+
+# 3. Merge adapter weights with base model
+merged_model = peft_model.merge_and_unload()
+```
+
+If you encounter size discrepancies in the saved model, ensure you're also saving the tokenizer:
+
+```python
+# Save both model and tokenizer
+tokenizer = AutoTokenizer.from_pretrained("base_model_name")
+merged_model.save_pretrained("path/to/save/merged_model")
+tokenizer.save_pretrained("path/to/save/merged_model")
+```
+
+> [!TIP]
+> ✏️ **Try it out!** Merge the adapter weights back into the base model. Use the `HuggingFaceTB/smoltalk` dataset to fine-tune a `deepseek-ai/DeepSeek-R1-Distill-Qwen-1.5B` model, using the LoRA configuration we defined above.
+
+
+# Resources
+
+- [LoRA: Low-Rank Adaptation of Large Language Models](https://arxiv.org/pdf/2106.09685)
+- [PEFT Documentation](https://huggingface.co/docs/peft)
+- [Hugging Face blog post on PEFT](https://huggingface.co/blog/peft)
diff --git a/chapters/en/chapter11/5.mdx b/chapters/en/chapter11/5.mdx
index 479521698..24b3c50b4 100644
--- a/chapters/en/chapter11/5.mdx
+++ b/chapters/en/chapter11/5.mdx
@@ -1,251 +1,251 @@
-# Evaluation
-
-With a finetuned model through either SFT or LoRA SFT, we should evaluate it on standard benchmarks. As machine learning engineers you should maintain a suite of relevant evaluations for your targeted domain of interest. In this page, we will look at some of the most common benchmarks and how to use them to evaluate your model. We'll also look at how to create custom benchmarks for your specific use case.
-
-## Automatic Benchmarks
-
-Automatic benchmarks serve as standardized tools for evaluating language models across different tasks and capabilities. While they provide a useful starting point for understanding model performance, it's important to recognize that they represent only one piece of a comprehensive evaluation strategy.
-
-## Understanding Automatic Benchmarks
-
-Automatic benchmarks typically consist of curated datasets with predefined tasks and evaluation metrics. These benchmarks aim to assess various aspects of model capability, from basic language understanding to complex reasoning. The key advantage of using automatic benchmarks is their standardization - they allow for consistent comparison across different models and provide reproducible results.
-
-However, it's crucial to understand that benchmark performance doesn't always translate directly to real-world effectiveness. A model that excels at academic benchmarks may still struggle with specific domain applications or practical use cases.
-
-## General Knowledge Benchmarks
-
-[MMLU](https://huggingface.co/datasets/cais/mmlu) (Massive Multitask Language Understanding) tests knowledge across 57 subjects, from science to humanities. While comprehensive, it may not reflect the depth of expertise needed for specific domains. TruthfulQA evaluates a model's tendency to reproduce common misconceptions, though it can't capture all forms of misinformation.
-
-## Reasoning Benchmarks
-
-[BBH](https://huggingface.co/datasets/lukaemon/bbh) (Big Bench Hard) and [GSM8K](https://huggingface.co/datasets/openai/gsm8k) focus on complex reasoning tasks. BBH tests logical thinking and planning, while GSM8K specifically targets mathematical problem-solving. These benchmarks help assess analytical capabilities but may not capture the nuanced reasoning required in real-world scenarios.
-
-## Language Understanding
-
-[HELM](https://github.com/stanford-crfm/helm) provides a holistic evaluation framework. Benchmarks like HELM offer insights into language processing capabilities on aspects like commonsense, world knowledge, and reasoning. But may not fully represent the complexity of natural conversation or domain-specific terminology.
-
-## Domain-Specific Benchmarks
-
-Let's look at a few benchmarks that focus on specific domains like math, coding, and chat.
-
-The [MATH benchmark](https://huggingface.co/papers/2103.03874) is another important evaluation tool for mathematical reasoning. It consists of 12,500 problems from mathematics competitions, covering algebra, geometry, number theory, counting, probability, and more. What makes MATH particularly challenging is that it requires multi-step reasoning, formal mathematical notation understanding, and the ability to generate step-by-step solutions. Unlike simpler arithmetic tasks, MATH problems often demand sophisticated problem-solving strategies and mathematical concept applications.
-
-The [HumanEval Benchmark](https://github.com/openai/human-eval) is a coding-focused evaluation dataset consisting of 164 programming problems. The benchmark tests a model's ability to generate functionally correct Python code that solves the given programming tasks. What makes HumanEval particularly valuable is that it evaluates both code generation capabilities and functional correctness through actual test case execution, rather than just superficial similarity to reference solutions. The problems range from basic string manipulation to more complex algorithms and data structures.
-
-[Alpaca Eval](https://tatsu-lab.github.io/alpaca_eval/) is an automated evaluation framework designed to assess the quality of instruction-following language models. It uses GPT-4 as a judge to evaluate model outputs across various dimensions including helpfulness, honesty, and harmlessness. The framework includes a dataset of 805 carefully curated prompts and can evaluate responses against multiple reference models like Claude, GPT-4, and others. What makes Alpaca Eval particularly useful is its ability to provide consistent, scalable evaluations without requiring human annotators, while still capturing nuanced aspects of model performance that traditional metrics might miss.
-
-## Alternative Evaluation Approaches
-
-Many organizations have developed alternative evaluation methods to address the limitations of standard benchmarks:
-
-### LLM-as-Judge
-
-Using one language model to evaluate another's outputs has become increasingly popular. This approach can provide more nuanced feedback than traditional metrics, though it comes with its own biases and limitations.
-
-### Evaluation Arenas
-
-Evaluation arenas like [Chatbot Arena](https://lmarena.ai/) offer a unique approach to LLM assessment through crowdsourced feedback. In these platforms, users engage in anonymous "battles" between two LLMs, asking questions and voting on which model provides better responses. This approach captures real-world usage patterns and preferences through diverse, challenging questions, with studies showing strong agreement between crowd-sourced votes and expert evaluations. While powerful, these platforms have limitations including potential user base bias, skewed prompt distributions, and a primary focus on helpfulness rather than safety considerations.
-
-### Custom Benchmark Suites
-
-Organizations often develop internal benchmark suites tailored to their specific needs and use cases. These might include domain-specific knowledge tests or evaluation scenarios that mirror actual deployment conditions.
-
-## Custom Evaluation
-
-While standard benchmarks provide a useful baseline, they shouldn't be your only evaluation method. Here's how to develop a more comprehensive approach:
-
-1. Start with relevant standard benchmarks to establish a baseline and enable comparison with other models.
-
-2. Identify the specific requirements and challenges of your use case. What tasks will your model actually perform? What kinds of errors would be most problematic?
-
-3. Develop custom evaluation datasets that reflect your actual use case. This might include:
- - Real user queries from your domain
- - Common edge cases you've encountered
- - Examples of particularly challenging scenarios
-
-4. Consider implementing a multi-layered evaluation strategy:
- - Automated metrics for quick feedback
- - Human evaluation for nuanced understanding
- - Domain expert review for specialized applications
- - A/B testing in controlled environments
-
-## Implementing Custom Evaluations
-
-In this section, we will implement evaluation for our finetuned model. We can use [`lighteval`](https://github.com/huggingface/lighteval) to evaluate our finetuned model on standard benchmarks, which contains a wide range of tasks built into the library. We just need to define the tasks we want to evaluate and the parameters for the evaluation.
-
-LightEval tasks are defined using a specific format:
-
-```
-{suite}|{task}|{num_few_shot}|{auto_reduce}
-```
-
-| Parameter | Description |
-|-----------|-------------|
-| `suite` | The benchmark suite (e.g., 'mmlu', 'truthfulqa') |
-| `task` | Specific task within the suite (e.g., 'abstract_algebra') |
-| `num_few_shot` | Number of examples to include in prompt (0 for zero-shot) |
-| `auto_reduce` | Whether to automatically reduce few-shot examples if prompt is too long (0 or 1) |
-
-Example: `"mmlu|abstract_algebra|0|0"` evaluates on MMLU's abstract algebra task with zero-shot inference.
-
-## Example Evaluation Pipeline
-
-Let's set up an evaluation pipeline for our finetuned model. We will evaluate the model on set of sub tasks that relate to the domain of medicine.
-
-Here's a complete example of evaluating on automatic benchmarks relevant to one specific domain using Lighteval with the VLLM backend:
-
-```bash
-lighteval accelerate \
- "pretrained=your-model-name" \
- "mmlu|anatomy|0|0" \
- "mmlu|high_school_biology|0|0" \
- "mmlu|high_school_chemistry|0|0" \
- "mmlu|professional_medicine|0|0" \
- --max_samples 40 \
- --batch_size 1 \
- --output_path "./results" \
- --save_generations true
-```
-
-Results are displayed in a tabular format showing:
-
-```
-| Task |Version|Metric|Value | |Stderr|
-|----------------------------------------|------:|------|-----:|---|-----:|
-|all | |acc |0.3333|± |0.1169|
-|leaderboard:mmlu:_average:5 | |acc |0.3400|± |0.1121|
-|leaderboard:mmlu:anatomy:5 | 0|acc |0.4500|± |0.1141|
-|leaderboard:mmlu:high_school_biology:5 | 0|acc |0.1500|± |0.0819|
-```
-
-Lighteval also include a python API for more detailed evaluation tasks, which is useful for manipulating the results in a more flexible way. Check out the [Lighteval documentation](https://huggingface.co/docs/lighteval/using-the-python-api) for more information.
-
-> [!TIP]
-> ✏️ **Try it out!** Evaluate your finetuned model on a specific task in lighteval.
-
-# End-of-chapter quiz[[end-of-chapter-quiz]]
-
-
-
-### 1. What are the main advantages of using automatic benchmarks for model evaluation?
-
-
-
-### 2. Which benchmark specifically tests knowledge across 57 different subjects?
-
-
-
-### 3. What is LLM-as-Judge?
-
-
-
-### 4. What should be included in a comprehensive evaluation strategy?
-
-
-
-### 5. What is a limitation of automatic benchmarks?
-
-
-
-### 6. What is the purpose of creating custom evaluation datasets?
-
-
+# Evaluation
+
+With a finetuned model through either SFT or LoRA SFT, we should evaluate it on standard benchmarks. As machine learning engineers you should maintain a suite of relevant evaluations for your targeted domain of interest. In this page, we will look at some of the most common benchmarks and how to use them to evaluate your model. We'll also look at how to create custom benchmarks for your specific use case.
+
+## Automatic Benchmarks
+
+Automatic benchmarks serve as standardized tools for evaluating language models across different tasks and capabilities. While they provide a useful starting point for understanding model performance, it's important to recognize that they represent only one piece of a comprehensive evaluation strategy.
+
+## Understanding Automatic Benchmarks
+
+Automatic benchmarks typically consist of curated datasets with predefined tasks and evaluation metrics. These benchmarks aim to assess various aspects of model capability, from basic language understanding to complex reasoning. The key advantage of using automatic benchmarks is their standardization - they allow for consistent comparison across different models and provide reproducible results.
+
+However, it's crucial to understand that benchmark performance doesn't always translate directly to real-world effectiveness. A model that excels at academic benchmarks may still struggle with specific domain applications or practical use cases.
+
+## General Knowledge Benchmarks
+
+[MMLU](https://huggingface.co/datasets/cais/mmlu) (Massive Multitask Language Understanding) tests knowledge across 57 subjects, from science to humanities. While comprehensive, it may not reflect the depth of expertise needed for specific domains. TruthfulQA evaluates a model's tendency to reproduce common misconceptions, though it can't capture all forms of misinformation.
+
+## Reasoning Benchmarks
+
+[BBH](https://huggingface.co/datasets/lukaemon/bbh) (Big Bench Hard) and [GSM8K](https://huggingface.co/datasets/openai/gsm8k) focus on complex reasoning tasks. BBH tests logical thinking and planning, while GSM8K specifically targets mathematical problem-solving. These benchmarks help assess analytical capabilities but may not capture the nuanced reasoning required in real-world scenarios.
+
+## Language Understanding
+
+[HELM](https://github.com/stanford-crfm/helm) provides a holistic evaluation framework. Benchmarks like HELM offer insights into language processing capabilities on aspects like commonsense, world knowledge, and reasoning. But may not fully represent the complexity of natural conversation or domain-specific terminology.
+
+## Domain-Specific Benchmarks
+
+Let's look at a few benchmarks that focus on specific domains like math, coding, and chat.
+
+The [MATH benchmark](https://huggingface.co/papers/2103.03874) is another important evaluation tool for mathematical reasoning. It consists of 12,500 problems from mathematics competitions, covering algebra, geometry, number theory, counting, probability, and more. What makes MATH particularly challenging is that it requires multi-step reasoning, formal mathematical notation understanding, and the ability to generate step-by-step solutions. Unlike simpler arithmetic tasks, MATH problems often demand sophisticated problem-solving strategies and mathematical concept applications.
+
+The [HumanEval Benchmark](https://github.com/openai/human-eval) is a coding-focused evaluation dataset consisting of 164 programming problems. The benchmark tests a model's ability to generate functionally correct Python code that solves the given programming tasks. What makes HumanEval particularly valuable is that it evaluates both code generation capabilities and functional correctness through actual test case execution, rather than just superficial similarity to reference solutions. The problems range from basic string manipulation to more complex algorithms and data structures.
+
+[Alpaca Eval](https://tatsu-lab.github.io/alpaca_eval/) is an automated evaluation framework designed to assess the quality of instruction-following language models. It uses GPT-4 as a judge to evaluate model outputs across various dimensions including helpfulness, honesty, and harmlessness. The framework includes a dataset of 805 carefully curated prompts and can evaluate responses against multiple reference models like Claude, GPT-4, and others. What makes Alpaca Eval particularly useful is its ability to provide consistent, scalable evaluations without requiring human annotators, while still capturing nuanced aspects of model performance that traditional metrics might miss.
+
+## Alternative Evaluation Approaches
+
+Many organizations have developed alternative evaluation methods to address the limitations of standard benchmarks:
+
+### LLM-as-Judge
+
+Using one language model to evaluate another's outputs has become increasingly popular. This approach can provide more nuanced feedback than traditional metrics, though it comes with its own biases and limitations.
+
+### Evaluation Arenas
+
+Evaluation arenas like [Chatbot Arena](https://lmarena.ai/) offer a unique approach to LLM assessment through crowdsourced feedback. In these platforms, users engage in anonymous "battles" between two LLMs, asking questions and voting on which model provides better responses. This approach captures real-world usage patterns and preferences through diverse, challenging questions, with studies showing strong agreement between crowd-sourced votes and expert evaluations. While powerful, these platforms have limitations including potential user base bias, skewed prompt distributions, and a primary focus on helpfulness rather than safety considerations.
+
+### Custom Benchmark Suites
+
+Organizations often develop internal benchmark suites tailored to their specific needs and use cases. These might include domain-specific knowledge tests or evaluation scenarios that mirror actual deployment conditions.
+
+## Custom Evaluation
+
+While standard benchmarks provide a useful baseline, they shouldn't be your only evaluation method. Here's how to develop a more comprehensive approach:
+
+1. Start with relevant standard benchmarks to establish a baseline and enable comparison with other models.
+
+2. Identify the specific requirements and challenges of your use case. What tasks will your model actually perform? What kinds of errors would be most problematic?
+
+3. Develop custom evaluation datasets that reflect your actual use case. This might include:
+ - Real user queries from your domain
+ - Common edge cases you've encountered
+ - Examples of particularly challenging scenarios
+
+4. Consider implementing a multi-layered evaluation strategy:
+ - Automated metrics for quick feedback
+ - Human evaluation for nuanced understanding
+ - Domain expert review for specialized applications
+ - A/B testing in controlled environments
+
+## Implementing Custom Evaluations
+
+In this section, we will implement evaluation for our finetuned model. We can use [`lighteval`](https://github.com/huggingface/lighteval) to evaluate our finetuned model on standard benchmarks, which contains a wide range of tasks built into the library. We just need to define the tasks we want to evaluate and the parameters for the evaluation.
+
+LightEval tasks are defined using a specific format:
+
+```
+{suite}|{task}|{num_few_shot}|{auto_reduce}
+```
+
+| Parameter | Description |
+|-----------|-------------|
+| `suite` | The benchmark suite (e.g., 'mmlu', 'truthfulqa') |
+| `task` | Specific task within the suite (e.g., 'abstract_algebra') |
+| `num_few_shot` | Number of examples to include in prompt (0 for zero-shot) |
+| `auto_reduce` | Whether to automatically reduce few-shot examples if prompt is too long (0 or 1) |
+
+Example: `"mmlu|abstract_algebra|0|0"` evaluates on MMLU's abstract algebra task with zero-shot inference.
+
+## Example Evaluation Pipeline
+
+Let's set up an evaluation pipeline for our finetuned model. We will evaluate the model on set of sub tasks that relate to the domain of medicine.
+
+Here's a complete example of evaluating on automatic benchmarks relevant to one specific domain using Lighteval with the VLLM backend:
+
+```bash
+lighteval accelerate \
+ "pretrained=your-model-name" \
+ "mmlu|anatomy|0|0" \
+ "mmlu|high_school_biology|0|0" \
+ "mmlu|high_school_chemistry|0|0" \
+ "mmlu|professional_medicine|0|0" \
+ --max_samples 40 \
+ --batch_size 1 \
+ --output_path "./results" \
+ --save_generations true
+```
+
+Results are displayed in a tabular format showing:
+
+```
+| Task |Version|Metric|Value | |Stderr|
+|----------------------------------------|------:|------|-----:|---|-----:|
+|all | |acc |0.3333|± |0.1169|
+|leaderboard:mmlu:_average:5 | |acc |0.3400|± |0.1121|
+|leaderboard:mmlu:anatomy:5 | 0|acc |0.4500|± |0.1141|
+|leaderboard:mmlu:high_school_biology:5 | 0|acc |0.1500|± |0.0819|
+```
+
+Lighteval also include a python API for more detailed evaluation tasks, which is useful for manipulating the results in a more flexible way. Check out the [Lighteval documentation](https://huggingface.co/docs/lighteval/using-the-python-api) for more information.
+
+> [!TIP]
+> ✏️ **Try it out!** Evaluate your finetuned model on a specific task in lighteval.
+
+# End-of-chapter quiz[[end-of-chapter-quiz]]
+
+
+
+### 1. What are the main advantages of using automatic benchmarks for model evaluation?
+
+
+
+### 2. Which benchmark specifically tests knowledge across 57 different subjects?
+
+
+
+### 3. What is LLM-as-Judge?
+
+
+
+### 4. What should be included in a comprehensive evaluation strategy?
+
+
+
+### 5. What is a limitation of automatic benchmarks?
+
+
+
+### 6. What is the purpose of creating custom evaluation datasets?
+
+
diff --git a/chapters/en/chapter11/6.mdx b/chapters/en/chapter11/6.mdx
index 093de47d6..7ddf87b6e 100644
--- a/chapters/en/chapter11/6.mdx
+++ b/chapters/en/chapter11/6.mdx
@@ -1,13 +1,13 @@
-# Conclusion
-
-In this chapter, we explored the essential components of fine-tuning language models:
-
-1. **Chat Templates** provide structure to model interactions, ensuring consistent and appropriate responses through standardized formatting.
-
-2. **Supervised Fine-Tuning (SFT)** allows adaptation of pre-trained models to specific tasks while maintaining their foundational knowledge.
-
-3. **LoRA** offers an efficient approach to fine-tuning by reducing trainable parameters while preserving model performance.
-
-4. **Evaluation** helps measure and validate the effectiveness of fine-tuning through various metrics and benchmarks.
-
-These techniques, when combined, enable the creation of specialized language models that can excel at specific tasks while remaining computationally efficient. Whether you're building a customer service bot or a domain-specific assistant, understanding these concepts is crucial for successful model adaptation.
+# Conclusion
+
+In this chapter, we explored the essential components of fine-tuning language models:
+
+1. **Chat Templates** provide structure to model interactions, ensuring consistent and appropriate responses through standardized formatting.
+
+2. **Supervised Fine-Tuning (SFT)** allows adaptation of pre-trained models to specific tasks while maintaining their foundational knowledge.
+
+3. **LoRA** offers an efficient approach to fine-tuning by reducing trainable parameters while preserving model performance.
+
+4. **Evaluation** helps measure and validate the effectiveness of fine-tuning through various metrics and benchmarks.
+
+These techniques, when combined, enable the creation of specialized language models that can excel at specific tasks while remaining computationally efficient. Whether you're building a customer service bot or a domain-specific assistant, understanding these concepts is crucial for successful model adaptation.
diff --git a/chapters/en/chapter11/7.mdx b/chapters/en/chapter11/7.mdx
index 0e644ed8b..5000d9a8c 100644
--- a/chapters/en/chapter11/7.mdx
+++ b/chapters/en/chapter11/7.mdx
@@ -1,33 +1,33 @@
-# Exam Time!
-
-It's time to put your knowledge to the test! We've prepared a short quiz for you to test your understanding of the concepts covered in this chapter.
-
-To take the quiz, you will need to follow these steps:
-
-1. Sign in to your Hugging Face account.
-2. Answer the questions in the quiz.
-3. Submit your answers.
-
-
-## Multiple Choice Quiz
-
-In this quiz, you will be asked to select the correct answer from a list of options. We'll test you on the fundamentals of supervised finetuning.
-
-
-
-
-## Code Quiz
-
-In this quiz, you will be asked to write code to complete a task. We'll test you on the code you've studied in the course from libraries like `datasets`, `transformers`, `peft`, and `TRL`.
-
-
+# Exam Time!
+
+It's time to put your knowledge to the test! We've prepared a short quiz for you to test your understanding of the concepts covered in this chapter.
+
+To take the quiz, you will need to follow these steps:
+
+1. Sign in to your Hugging Face account.
+2. Answer the questions in the quiz.
+3. Submit your answers.
+
+
+## Multiple Choice Quiz
+
+In this quiz, you will be asked to select the correct answer from a list of options. We'll test you on the fundamentals of supervised finetuning.
+
+
+
+
+## Code Quiz
+
+In this quiz, you will be asked to write code to complete a task. We'll test you on the code you've studied in the course from libraries like `datasets`, `transformers`, `peft`, and `TRL`.
+
+
diff --git a/chapters/en/chapter12/1.mdx b/chapters/en/chapter12/1.mdx
index 773f2c808..7a93676ae 100644
--- a/chapters/en/chapter12/1.mdx
+++ b/chapters/en/chapter12/1.mdx
@@ -1,90 +1,90 @@
-# Open R1 for Students
-
-Welcome to an exciting journey into the world of open-source AI with reinforcement learning! This chapter is designed to help students understand reinforcement learning and its role in LLMs.
-
-We will also explore [Open R1](https://github.com/huggingface/open-r1), a groundbreaking community project that's making advanced AI accessible to everyone. Specifically, this course is to help students and learners to use and contribute to [Open R1](https://github.com/huggingface/open-r1).
-
-## What You'll Learn
-
-In this chapter, we'll break down complex concepts into easy-to-understand pieces and show you how you can be part of this exciting project to make LLMs reason on complex problems.
-
-LLMs have shown excellent performance on many generative tasks. However, up until recently they have struggled on complex problems that require reasoning. For example, they struggle to deal with puzzles or math problems that require multiple steps of reasoning.
-
-Open R1 is a project that aims to make LLMs reason on complex problems. It does this by using reinforcement learning to encourage LLMs to 'think' and reason.
-
-In simple terms, the model is trained to generate thoughts as well as outputs, and to structure these thoughts and outputs so that they can be handled separately by the user.
-
-Let's take a look at an example. As we gave ourself the task of solving the following problem, we might think like this:
-
-```sh
-Problem: "I have 3 apples and 2 oranges. How many pieces of fruit do I have in total?"
-
-Thought: "I need to add the number of apples and oranges to get the total number of pieces of fruit."
-
-Answer: "5"
-```
-
-We can then structure this thought and answer so that they can be handled separately by the user. For reasoning tasks, LLMs can be trained to generate thoughts and answers in the following format:
-
-```sh
-I need to add the number of apples and oranges to get the total number of pieces of fruit.
-5
-```
-
-As a user, we can then extract the thought and answer from the model's output and use them to solve the problem.
-
-## Why This Matters for Students
-
-As a student, understanding Open R1 and the role of reinforcement learning in LLMs is valuable because:
-- It shows you how cutting-edge AI is developed
-- It gives you hands-on opportunities to learn and contribute
-- It helps you understand where AI technology is heading
-- It opens doors to future career opportunities in AI
-
-## Chapter Overview
-
-This chapter is divided into four sections, each focusing on a different aspect of Open R1:
-
-### 1️⃣ Introduction to Reinforcement Learning and its Role in LLMs
-We'll explore the basics of Reinforcement Learning (RL) and its role in training LLMs.
-- What is RL?
-- How is RL used in LLMs?
-- What is DeepSeek R1?
-- What are the key innovations of DeepSeek R1?
-
-### 2️⃣ Understanding the DeepSeek R1 Paper
-We'll break down the research paper that inspired [Open R1](https://huggingface.co/open-r1):
-- Key innovations and breakthroughs
-- The training process and architecture
-- Results and their significance
-
-### 3️⃣ Implementing GRPO in TRL
-We'll get practical with code examples:
-- How to use the Transformer Reinforcement Learning (TRL) library
-- Setting up GRPO training
-
-### 4️⃣ Practical use case to align a model
-We'll look at a practical use case to align a model using Open R1.
-- How to train a model using GRPO in TRL
-- Share your model on the [Hugging Face Hub](https://huggingface.co/models)
-
-## Prerequisites
-
-To get the most out of this chapter, it's helpful to have:
-- Solid understanding of Python programming
-- Familiarity with machine learning concepts
-- Interest in AI and language models
-
-Don't worry if you're missing some of these – we'll explain key concepts as we go along! 🚀
-
-> [!TIP]
-> If you don't have all the prerequisites, check out this [course](/course/chapter1/1) from units 1 to 11
-
-## How to Use This Chapter
-
-1. **Read Sequentially**: The sections build on each other, so it's best to read them in order
-2. **Share Notes**: Write down key concepts and questions and discuss them within the community in [Discord](https://discord.gg/UrrTSsSyjb)
-3. **Try the Code**: When we get to practical examples, try them yourself
-4. **Join the Community**: Use the resources we provide to connect with other learners
-
-Let's begin our exploration of Open R1 and discover how you can be part of making AI more accessible to everyone! 🚀
+# Open R1 for Students
+
+Welcome to an exciting journey into the world of open-source AI with reinforcement learning! This chapter is designed to help students understand reinforcement learning and its role in LLMs.
+
+We will also explore [Open R1](https://github.com/huggingface/open-r1), a groundbreaking community project that's making advanced AI accessible to everyone. Specifically, this course is to help students and learners to use and contribute to [Open R1](https://github.com/huggingface/open-r1).
+
+## What You'll Learn
+
+In this chapter, we'll break down complex concepts into easy-to-understand pieces and show you how you can be part of this exciting project to make LLMs reason on complex problems.
+
+LLMs have shown excellent performance on many generative tasks. However, up until recently they have struggled on complex problems that require reasoning. For example, they struggle to deal with puzzles or math problems that require multiple steps of reasoning.
+
+Open R1 is a project that aims to make LLMs reason on complex problems. It does this by using reinforcement learning to encourage LLMs to 'think' and reason.
+
+In simple terms, the model is trained to generate thoughts as well as outputs, and to structure these thoughts and outputs so that they can be handled separately by the user.
+
+Let's take a look at an example. As we gave ourself the task of solving the following problem, we might think like this:
+
+```sh
+Problem: "I have 3 apples and 2 oranges. How many pieces of fruit do I have in total?"
+
+Thought: "I need to add the number of apples and oranges to get the total number of pieces of fruit."
+
+Answer: "5"
+```
+
+We can then structure this thought and answer so that they can be handled separately by the user. For reasoning tasks, LLMs can be trained to generate thoughts and answers in the following format:
+
+```sh
+I need to add the number of apples and oranges to get the total number of pieces of fruit.
+5
+```
+
+As a user, we can then extract the thought and answer from the model's output and use them to solve the problem.
+
+## Why This Matters for Students
+
+As a student, understanding Open R1 and the role of reinforcement learning in LLMs is valuable because:
+- It shows you how cutting-edge AI is developed
+- It gives you hands-on opportunities to learn and contribute
+- It helps you understand where AI technology is heading
+- It opens doors to future career opportunities in AI
+
+## Chapter Overview
+
+This chapter is divided into four sections, each focusing on a different aspect of Open R1:
+
+### 1️⃣ Introduction to Reinforcement Learning and its Role in LLMs
+We'll explore the basics of Reinforcement Learning (RL) and its role in training LLMs.
+- What is RL?
+- How is RL used in LLMs?
+- What is DeepSeek R1?
+- What are the key innovations of DeepSeek R1?
+
+### 2️⃣ Understanding the DeepSeek R1 Paper
+We'll break down the research paper that inspired [Open R1](https://huggingface.co/open-r1):
+- Key innovations and breakthroughs
+- The training process and architecture
+- Results and their significance
+
+### 3️⃣ Implementing GRPO in TRL
+We'll get practical with code examples:
+- How to use the Transformer Reinforcement Learning (TRL) library
+- Setting up GRPO training
+
+### 4️⃣ Practical use case to align a model
+We'll look at a practical use case to align a model using Open R1.
+- How to train a model using GRPO in TRL
+- Share your model on the [Hugging Face Hub](https://huggingface.co/models)
+
+## Prerequisites
+
+To get the most out of this chapter, it's helpful to have:
+- Solid understanding of Python programming
+- Familiarity with machine learning concepts
+- Interest in AI and language models
+
+Don't worry if you're missing some of these – we'll explain key concepts as we go along! 🚀
+
+> [!TIP]
+> If you don't have all the prerequisites, check out this [course](/course/chapter1/1) from units 1 to 11
+
+## How to Use This Chapter
+
+1. **Read Sequentially**: The sections build on each other, so it's best to read them in order
+2. **Share Notes**: Write down key concepts and questions and discuss them within the community in [Discord](https://discord.gg/UrrTSsSyjb)
+3. **Try the Code**: When we get to practical examples, try them yourself
+4. **Join the Community**: Use the resources we provide to connect with other learners
+
+Let's begin our exploration of Open R1 and discover how you can be part of making AI more accessible to everyone! 🚀
diff --git a/chapters/en/chapter12/2.mdx b/chapters/en/chapter12/2.mdx
index 0d4743967..15f82cf27 100644
--- a/chapters/en/chapter12/2.mdx
+++ b/chapters/en/chapter12/2.mdx
@@ -1,240 +1,240 @@
-# Introduction to Reinforcement Learning and its Role in LLMs
-
-Welcome to the first page!
-
-We're going to start our journey into the exciting world of Reinforcement Learning (RL) and discover how it's revolutionizing the way we train Language Models like the ones you might use every day.
-
-> [!TIP]
-> In this chapter, we are focusing on reinforcement learning for language models. However, reinforcement learning is a broad field with many applications beyond language models. If you're interested in learning more about reinforcement learning, you should check out the [Deep Reinforcement Learning course](https://huggingface.co/courses/deep-rl-course/en/unit1/introduction).
-
-This page will give you a friendly and clear introduction to RL, even if you've never encountered it before. We'll break down the core ideas and see why RL is becoming so important in the field of Large Language Models (LLMs).
-
-## What is Reinforcement Learning (RL)?
-
-Imagine you're training a dog. You want to teach it to sit. You might say "Sit!" and then, if the dog sits, you give it a treat and praise. If it doesn't sit, you might gently guide it or just try again. Over time, the dog learns to associate sitting with the positive reward (treat and praise) and is more likely to sit when you say "Sit!" again. In reinforcement learning, we refer to this feedback as a **reward**.
-
-That, in a nutshell, is the basic idea behind Reinforcement Learning! Instead of a dog, we have a **language model** (in reinforcement learning, we call it an **agent**), and instead of you, we have the **environment** that gives feedback.
-
-
-
-Let's break down the key pieces of RL:
-
-### Agent
-
-This is our learner. In the dog example, the dog is the agent. In the context of LLMs, the LLM itself becomes the agent we want to train. The agent is the one making decisions and learning from the environment and its rewards.
-
-### Environment
-
-This is the world the agent lives in and interacts with. For the dog, the environment is your house and you. For an LLM, the environment is a bit more abstract – it could be the users it interacts with, or a simulated scenario we set up for it. The environment provides feedback to the agent.
-
-### Action
-
-These are the choices the agent can make in the environment. The dog's actions are things like "sit", "stand", "bark", etc. For an LLM, actions could be generating words in a sentence, choosing which answer to give to a question, or deciding how to respond in a conversation.
-
-### Reward
-
-This is the feedback the environment gives to the agent after it takes an action. Rewards are usually numbers.
-
-**Positive rewards** are like treats and praise – they tell the agent "good job, you did something right!".
-
-**Negative rewards** (or penalties) are like a gentle "no" – they tell the agent "that wasn't quite right, try something else". For the dog, the treat is the reward.
-
-For an LLM, rewards are designed to reflect how well the LLM is doing at a specific task – maybe it's how helpful, truthful, or harmless its response is.
-
-### Policy
-
-This is the agent's strategy for choosing actions. It's like the dog's understanding of what it should do when you say "Sit!". In RL, the policy is what we're really trying to learn and improve. It's a set of rules or a function that tells the agent what action to take in different situations. Initially, the policy might be random, but as the agent learns, the policy becomes better at choosing actions that lead to higher rewards.
-
-## The RL Process: Trial and Error
-
-
-
-Reinforcement Learning happens through a process of trial and error:
-
-| Step | Process | Description |
-|------|---------|-------------|
-| 1. Observation | The agent observes the environment | The agent takes in information about its current state and surroundings |
-| 2. Action | The agent takes an action based on its current policy | Using its learned strategy (policy), the agent decides what to do next |
-| 3. Feedback | The environment gives the agent a reward | The agent receives feedback on how good or bad its action was |
-| 4. Learning | The agent updates its policy based on the reward | The agent adjusts its strategy - reinforcing actions that led to high rewards and avoiding those that led low rewards |
-| 5. Iteration | Repeat the process | This cycle continues, allowing the agent to continuously improve its decision-making |
-
-Think about learning to ride a bike. You might wobble and fall at first (negative reward!). But when you manage to balance and pedal smoothly, you feel good (positive reward!). You adjust your actions based on this feedback – leaning slightly, pedaling faster, etc. – until you learn to ride well. RL is similar – it's about learning through interaction and feedback.
-
-## Role of RL in Large Language Models (LLMs)
-
-Now, why is RL so important for Large Language Models?
-
-Well, training really good LLMs is tricky. We can train them on massive amounts of text from the internet, and they become very good at predicting the next word in a sentence. This is how they learn to generate fluent and grammatically correct text, as we learned in [chapter 2](/course/chapter2/1).
-
-However, just being fluent isn't enough. We want our LLMs to be more than just good at stringing words together. We want them to be:
-
-* **Helpful:** Provide useful and relevant information.
-* **Harmless:** Avoid generating toxic, biased, or harmful content.
-* **Aligned with Human Preferences:** Respond in ways that humans find natural, helpful, and engaging.
-
-Pre-training LLM methods, which mostly rely on predicting the next word from text data, sometimes fall short on these aspects.
-
-Whilst supervised training is excellent at producing structured outputs, it can be less effective at producing helpful, harmless, and aligned responses. We explore supervised training in [chapter 11](/course/chapter11/1).
-
-Fine-tuned models might generate fluent and structured text that is still factually incorrect, biased, or doesn't really answer the user's question in a helpful way.
-
-**Enter Reinforcement Learning\!** RL gives us a way to fine-tune these pre-trained LLMs to better achieve these desired qualities. It's like giving our LLM dog extra training to become a well-behaved and helpful companion, not just a dog that knows how to bark fluently\!
-
-## Reinforcement Learning from Human Feedback (RLHF)
-
-A very popular technique for aligning language models is **Reinforcement Learning from Human Feedback (RLHF)**. In RLHF, we use human feedback as a proxy for the "reward" signal in RL. Here's how it works:
-
-1. **Get Human Preferences:** We might ask humans to compare different responses generated by the LLM for the same input prompt and tell us which response they prefer. For example, we might show a human two different answers to the question "What is the capital of France?" and ask them "Which answer is better?".
-
-2. **Train a Reward Model:** We use this human preference data to train a separate model called a **reward model**. This reward model learns to predict what kind of responses humans will prefer. It learns to score responses based on helpfulness, harmlessness, and alignment with human preferences.
-
-3. **Fine-tune the LLM with RL:** Now we use the reward model as the environment for our LLM agent. The LLM generates responses (actions), and the reward model scores these responses (provides rewards). In essence, we're training the LLM to produce text that our reward model (which learned from human preferences) thinks is good.
-
-
-
-From a general perspective, let's look at the benefits of using RL in LLMs:
-
-| Benefit | Description |
-|---------|-------------|
-| Improved Control | RL allows us to have more control over the kind of text LLMs generate. We can guide them to produce text that is more aligned with specific goals, like being helpful, creative, or concise. |
-| Enhanced Alignment with Human Values | RLHF, in particular, helps us align LLMs with complex and often subjective human preferences. It's hard to write down rules for "what makes a good answer," but humans can easily judge and compare responses. RLHF lets the model learn from these human judgments. |
-| Mitigating Undesirable Behaviors | RL can be used to reduce negative behaviors in LLMs, such as generating toxic language, spreading misinformation, or exhibiting biases. By designing rewards that penalize these behaviors, we can nudge the model to avoid them. |
-
-Reinforcement Learning from Human Feedback has been used to train many of the most popular LLMs today, such as OpenAI's GPT-4, Google's Gemini, and DeepSeek's R1. There are a wide range of techniques for RLHF, with varying degrees of complexity and sophistication. In this chapter, we will focus on Group Relative Policy Optimization (GRPO), which is a technique for RLHF that has been shown to be effective at training LLMs that are helpful, harmless, and aligned with human preferences.
-
-## Why should we care about GRPO (Group Relative Policy Optimization)?
-
-There are many techniques for RLHF but this course is focused on GRPO because it represents a significant advancement in reinforcement learning for language models.
-
-Let's briefly consider two of other popular techniques for RLHF:
-
-- Proximal Policy Optimization (PPO)
-- Direct Preference Optimization (DPO)
-
-Proximal Policy Optimization (PPO) was one of the first highly effective techniques for RLHF. It uses a policy gradient method to update the policy based on the reward from a separate reward model.
-
-Direct Preference Optimization (DPO) was later developed as a simpler technique that eliminates the need for a separate reward model using preference data directly. Essentially, framing the problem as a classification task between the chosen and rejected responses.
-
-> [!TIP]
-> DPO and PPO are complex reinforcement learning algorithms in their own right, which we will not cover in this course. If you're interested in learning more about them, you can check out the following resources:
->
-> - [Proximal Policy Optimization](https://huggingface.co/docs/trl/main/en/ppo_trainer)
-> - [Direct Preference Optimization](https://huggingface.co/docs/trl/main/en/dpo_trainer)
-
-Unlike DPO and PPO, GRPO groups similar samples together and compares them as a group. The group-based approach provides more stable gradients and better convergence properties compared to other methods.
-
-GRPO does not use preference data like DPO, but instead compares groups of similar samples using a reward signal from a model or function.
-
-GRPO is flexible in how it obtains reward signals - it can work with a reward model (like PPO does) but doesn't strictly require one. This is because GRPO can incorporate reward signals from any function or model that can evaluate the quality of responses.
-
-For example, we could use a length function to reward shorter responses, a mathematical solver to verify solution correctness, or a factual correctness function to reward responses that are more factually accurate. This flexibility makes GRPO particularly versatile for different types of alignment tasks.
-
----
-
-Congratulations on completing Module 1\! You've now got a solid introduction to Reinforcement Learning and its crucial role in shaping the future of Large Language Models. You understand the basic concepts of RL, why it's used for LLMs, and you've been introduced to GRPO, a key algorithm in this field.
-
-In the next module, we'll get our hands dirty and dive into the DeepSeek R1 paper to see these concepts in action\!
-
-## Quiz
-
-### 1. What are the key components of Reinforcement Learning?
-
-
-
-### 2. What is the main advantage of RLHF for training language models?
-
-
-
-### 3. In the context of RL for LLMs, what represents an "action"?
-
-
-
-### 4. What is the role of the reward in RL training of language models?
-
-
-
-### 5. What is a reward in the context of RL for LLMs?
-
-
-
+# Introduction to Reinforcement Learning and its Role in LLMs
+
+Welcome to the first page!
+
+We're going to start our journey into the exciting world of Reinforcement Learning (RL) and discover how it's revolutionizing the way we train Language Models like the ones you might use every day.
+
+> [!TIP]
+> In this chapter, we are focusing on reinforcement learning for language models. However, reinforcement learning is a broad field with many applications beyond language models. If you're interested in learning more about reinforcement learning, you should check out the [Deep Reinforcement Learning course](https://huggingface.co/courses/deep-rl-course/en/unit1/introduction).
+
+This page will give you a friendly and clear introduction to RL, even if you've never encountered it before. We'll break down the core ideas and see why RL is becoming so important in the field of Large Language Models (LLMs).
+
+## What is Reinforcement Learning (RL)?
+
+Imagine you're training a dog. You want to teach it to sit. You might say "Sit!" and then, if the dog sits, you give it a treat and praise. If it doesn't sit, you might gently guide it or just try again. Over time, the dog learns to associate sitting with the positive reward (treat and praise) and is more likely to sit when you say "Sit!" again. In reinforcement learning, we refer to this feedback as a **reward**.
+
+That, in a nutshell, is the basic idea behind Reinforcement Learning! Instead of a dog, we have a **language model** (in reinforcement learning, we call it an **agent**), and instead of you, we have the **environment** that gives feedback.
+
+
+
+Let's break down the key pieces of RL:
+
+### Agent
+
+This is our learner. In the dog example, the dog is the agent. In the context of LLMs, the LLM itself becomes the agent we want to train. The agent is the one making decisions and learning from the environment and its rewards.
+
+### Environment
+
+This is the world the agent lives in and interacts with. For the dog, the environment is your house and you. For an LLM, the environment is a bit more abstract – it could be the users it interacts with, or a simulated scenario we set up for it. The environment provides feedback to the agent.
+
+### Action
+
+These are the choices the agent can make in the environment. The dog's actions are things like "sit", "stand", "bark", etc. For an LLM, actions could be generating words in a sentence, choosing which answer to give to a question, or deciding how to respond in a conversation.
+
+### Reward
+
+This is the feedback the environment gives to the agent after it takes an action. Rewards are usually numbers.
+
+**Positive rewards** are like treats and praise – they tell the agent "good job, you did something right!".
+
+**Negative rewards** (or penalties) are like a gentle "no" – they tell the agent "that wasn't quite right, try something else". For the dog, the treat is the reward.
+
+For an LLM, rewards are designed to reflect how well the LLM is doing at a specific task – maybe it's how helpful, truthful, or harmless its response is.
+
+### Policy
+
+This is the agent's strategy for choosing actions. It's like the dog's understanding of what it should do when you say "Sit!". In RL, the policy is what we're really trying to learn and improve. It's a set of rules or a function that tells the agent what action to take in different situations. Initially, the policy might be random, but as the agent learns, the policy becomes better at choosing actions that lead to higher rewards.
+
+## The RL Process: Trial and Error
+
+
+
+Reinforcement Learning happens through a process of trial and error:
+
+| Step | Process | Description |
+|------|---------|-------------|
+| 1. Observation | The agent observes the environment | The agent takes in information about its current state and surroundings |
+| 2. Action | The agent takes an action based on its current policy | Using its learned strategy (policy), the agent decides what to do next |
+| 3. Feedback | The environment gives the agent a reward | The agent receives feedback on how good or bad its action was |
+| 4. Learning | The agent updates its policy based on the reward | The agent adjusts its strategy - reinforcing actions that led to high rewards and avoiding those that led low rewards |
+| 5. Iteration | Repeat the process | This cycle continues, allowing the agent to continuously improve its decision-making |
+
+Think about learning to ride a bike. You might wobble and fall at first (negative reward!). But when you manage to balance and pedal smoothly, you feel good (positive reward!). You adjust your actions based on this feedback – leaning slightly, pedaling faster, etc. – until you learn to ride well. RL is similar – it's about learning through interaction and feedback.
+
+## Role of RL in Large Language Models (LLMs)
+
+Now, why is RL so important for Large Language Models?
+
+Well, training really good LLMs is tricky. We can train them on massive amounts of text from the internet, and they become very good at predicting the next word in a sentence. This is how they learn to generate fluent and grammatically correct text, as we learned in [chapter 2](/course/chapter2/1).
+
+However, just being fluent isn't enough. We want our LLMs to be more than just good at stringing words together. We want them to be:
+
+* **Helpful:** Provide useful and relevant information.
+* **Harmless:** Avoid generating toxic, biased, or harmful content.
+* **Aligned with Human Preferences:** Respond in ways that humans find natural, helpful, and engaging.
+
+Pre-training LLM methods, which mostly rely on predicting the next word from text data, sometimes fall short on these aspects.
+
+Whilst supervised training is excellent at producing structured outputs, it can be less effective at producing helpful, harmless, and aligned responses. We explore supervised training in [chapter 11](/course/chapter11/1).
+
+Fine-tuned models might generate fluent and structured text that is still factually incorrect, biased, or doesn't really answer the user's question in a helpful way.
+
+**Enter Reinforcement Learning\!** RL gives us a way to fine-tune these pre-trained LLMs to better achieve these desired qualities. It's like giving our LLM dog extra training to become a well-behaved and helpful companion, not just a dog that knows how to bark fluently\!
+
+## Reinforcement Learning from Human Feedback (RLHF)
+
+A very popular technique for aligning language models is **Reinforcement Learning from Human Feedback (RLHF)**. In RLHF, we use human feedback as a proxy for the "reward" signal in RL. Here's how it works:
+
+1. **Get Human Preferences:** We might ask humans to compare different responses generated by the LLM for the same input prompt and tell us which response they prefer. For example, we might show a human two different answers to the question "What is the capital of France?" and ask them "Which answer is better?".
+
+2. **Train a Reward Model:** We use this human preference data to train a separate model called a **reward model**. This reward model learns to predict what kind of responses humans will prefer. It learns to score responses based on helpfulness, harmlessness, and alignment with human preferences.
+
+3. **Fine-tune the LLM with RL:** Now we use the reward model as the environment for our LLM agent. The LLM generates responses (actions), and the reward model scores these responses (provides rewards). In essence, we're training the LLM to produce text that our reward model (which learned from human preferences) thinks is good.
+
+
+
+From a general perspective, let's look at the benefits of using RL in LLMs:
+
+| Benefit | Description |
+|---------|-------------|
+| Improved Control | RL allows us to have more control over the kind of text LLMs generate. We can guide them to produce text that is more aligned with specific goals, like being helpful, creative, or concise. |
+| Enhanced Alignment with Human Values | RLHF, in particular, helps us align LLMs with complex and often subjective human preferences. It's hard to write down rules for "what makes a good answer," but humans can easily judge and compare responses. RLHF lets the model learn from these human judgments. |
+| Mitigating Undesirable Behaviors | RL can be used to reduce negative behaviors in LLMs, such as generating toxic language, spreading misinformation, or exhibiting biases. By designing rewards that penalize these behaviors, we can nudge the model to avoid them. |
+
+Reinforcement Learning from Human Feedback has been used to train many of the most popular LLMs today, such as OpenAI's GPT-4, Google's Gemini, and DeepSeek's R1. There are a wide range of techniques for RLHF, with varying degrees of complexity and sophistication. In this chapter, we will focus on Group Relative Policy Optimization (GRPO), which is a technique for RLHF that has been shown to be effective at training LLMs that are helpful, harmless, and aligned with human preferences.
+
+## Why should we care about GRPO (Group Relative Policy Optimization)?
+
+There are many techniques for RLHF but this course is focused on GRPO because it represents a significant advancement in reinforcement learning for language models.
+
+Let's briefly consider two of other popular techniques for RLHF:
+
+- Proximal Policy Optimization (PPO)
+- Direct Preference Optimization (DPO)
+
+Proximal Policy Optimization (PPO) was one of the first highly effective techniques for RLHF. It uses a policy gradient method to update the policy based on the reward from a separate reward model.
+
+Direct Preference Optimization (DPO) was later developed as a simpler technique that eliminates the need for a separate reward model using preference data directly. Essentially, framing the problem as a classification task between the chosen and rejected responses.
+
+> [!TIP]
+> DPO and PPO are complex reinforcement learning algorithms in their own right, which we will not cover in this course. If you're interested in learning more about them, you can check out the following resources:
+>
+> - [Proximal Policy Optimization](https://huggingface.co/docs/trl/main/en/ppo_trainer)
+> - [Direct Preference Optimization](https://huggingface.co/docs/trl/main/en/dpo_trainer)
+
+Unlike DPO and PPO, GRPO groups similar samples together and compares them as a group. The group-based approach provides more stable gradients and better convergence properties compared to other methods.
+
+GRPO does not use preference data like DPO, but instead compares groups of similar samples using a reward signal from a model or function.
+
+GRPO is flexible in how it obtains reward signals - it can work with a reward model (like PPO does) but doesn't strictly require one. This is because GRPO can incorporate reward signals from any function or model that can evaluate the quality of responses.
+
+For example, we could use a length function to reward shorter responses, a mathematical solver to verify solution correctness, or a factual correctness function to reward responses that are more factually accurate. This flexibility makes GRPO particularly versatile for different types of alignment tasks.
+
+---
+
+Congratulations on completing Module 1\! You've now got a solid introduction to Reinforcement Learning and its crucial role in shaping the future of Large Language Models. You understand the basic concepts of RL, why it's used for LLMs, and you've been introduced to GRPO, a key algorithm in this field.
+
+In the next module, we'll get our hands dirty and dive into the DeepSeek R1 paper to see these concepts in action\!
+
+## Quiz
+
+### 1. What are the key components of Reinforcement Learning?
+
+
+
+### 2. What is the main advantage of RLHF for training language models?
+
+
+
+### 3. In the context of RL for LLMs, what represents an "action"?
+
+
+
+### 4. What is the role of the reward in RL training of language models?
+
+
+
+### 5. What is a reward in the context of RL for LLMs?
+
+
+
diff --git a/chapters/en/chapter12/3.mdx b/chapters/en/chapter12/3.mdx
index fd9d5583d..7f0fc2153 100644
--- a/chapters/en/chapter12/3.mdx
+++ b/chapters/en/chapter12/3.mdx
@@ -1,329 +1,329 @@
-# Understanding the DeepSeek R1 Paper
-
-This chapter is a crash course paper reading. We will walk through the paper in simple terms, and then we will break down the key concepts and takeaways.
-
-DeepSeek R1 represents a significant advancement in language model training, particularly in developing reasoning capabilities through reinforcement learning. The paper introduces a new reinforcement learning algorithm called Group Relative Policy Optimization (GRPO).
-
-
-
-In the next chapter, we will build on this knowledge and implement GRPO in practice.
-
-The initial goal of the paper was to explore whether pure reinforcement learning could develop reasoning capabilities without supervised fine-tuning.
-
-> [!TIP]
-> Up until that point, all the popular LLMs required some supervised fine-tuning, which we explored in [chapter 11](/course/chapter11/1).
-
-## The Breakthrough 'Aha' Moment
-
-
-
-One of the most remarkable discoveries in R1-Zero's training was the emergence of a phenomenon known as the "Aha Moment." This phenomenon is somewhat similar to how humans experience sudden realizations while problem-solving. Here's how it works:
-
-1. Initial Attempt: The model makes an initial attempt at solving a problem
-2. Recognition: It recognizes potential errors or inconsistencies
-3. Self-Correction: It adjusts its approach based on this recognition
-4. Explanation: It can explain why the new approach is better
-
-This breakthrough resonates with learners and feels like a "Eureka" moment. It demonstrates learning rather than mere memorization, so let's take a moment to imagine what it feels like to have an "Aha" moment.
-
-For example, imagine you're trying to solve a puzzle:
-- First try: "This piece should go here based on the color"
-- Recognition: "But wait, the shape doesn't quite fit"
-- Correction: "Ah, it actually belongs over there"
-- Explanation: "Because both the color and shape pattern match in this position"
-
-This ability emerged naturally from RL training, without being explicitly programmed, demonstrating learning rather than mere memorization of a process from the training data.
-
-The easiest way to understand the 'Aha' moment is to see it in action. Let's take a look at an example. In the chat below, we ask the model to solve a problem and the UI shows the model's thought process as it solves the problem.
-
-
-
-If you want to try Deepseek's R1, you can also check out [Hugging Chat](https://huggingface.co/chat/models/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B).
-
-## The Training Process
-
-Training R1 was a multi-phase process. Let's break down the phases and the key innovations in each phase.
-
-The final process results in two models:
-- DeepSeek-R1-Zero: A model trained purely using reinforcement learning.
-- DeepSeek-R1: A model that builds on the foundation of DeepSeek-R1-Zero and adds supervised fine-tuning.
-
-| Feature | DeepSeek-R1-Zero | DeepSeek-R1 |
-|---------|------------------|--------------|
-| Training Approach | Pure RL | Multi-phase (SFT + RL) |
-| Fine-tuning | None | Supervised fine-tuning |
-| Reasoning Capability | Emergent | Enhanced |
-| AIME Performance | 71.0% | 79.8% |
-| Key Characteristics | Strong reasoning but readability issues | Better language consistency and readability |
-
-While DeepSeek-R1-Zero demonstrates the potential of pure reinforcement learning for developing reasoning capabilities, DeepSeek-R1 builds upon this foundation with a more balanced approach that prioritizes both reasoning performance and usability.
-
-The training process involves four phases:
-
-1. Cold Start Phase
-2. Reasoning RL Phase
-3. Rejection Sampling Phase
-4. Diverse RL Phase
-
-Let's break down each phase:
-
-### Cold Start Phase (Quality Foundation)
-
-
-
-This phase is designed to establish a strong foundation for the model's readability and response quality. It uses a small dataset of high-quality samples from R1-Zero to fine-tune the V3-Base model. Starting with the DeepSeek-V3-Base model, the team used thousands of validated, high-quality samples from R1-Zero for supervised fine-tuning. This innovative approach uses a small but high quality dataset to establish strong baseline readability and response quality.
-
-### Reasoning RL Phase (Capability Building)
-
-
-
-The Reasoning RL Phase focuses on developing core reasoning capabilities across domains including mathematics, coding, science, and logic. This phase employs rule-based reinforcement learning, with rewards directly tied to solution correctness.
-
-Crucially, all the tasks in this phase are 'verifiable' so we can check if the model's answer is correct or not. For example, in the case of mathematics, we can check if the model's answer is correct by using a mathematical solver.
-
-What makes this phase particularly innovative is its direct optimization approach that eliminates the need for a separate reward model, streamlining the training process.
-
-### Rejection Sampling Phase (Quality Control)
-
-
-
-During the Rejection Sampling Phase, the model generates samples which are then filtered through a quality control process. DeepSeek-V3 serves as the quality judge, evaluating outputs across a broad scope that extends beyond pure reasoning tasks. The filtered data is then used for supervised fine-tuning. This phase's innovation lies in its ability to combine multiple quality signals to ensure high-standard outputs.
-
-### Diverse RL Phase (Broad Alignment)
-
-
-
-The final Diverse RL Phase tackles multiple task types using a sophisticated hybrid approach. For deterministic tasks, it employs rule-based rewards, while subjective tasks are evaluated through LLM feedback. This phase aims to achieve human preference alignment through its innovative hybrid reward approach, combining the precision of rule-based systems with the flexibility of language model evaluation.
-
-## The Algorithm: Group Relative Policy Optimization (GRPO)
-
-Now that we have a good understanding of the training process, let's look at the algorithm that was used to train the model.
-
-The authors describe GRPO as a breakthrough in model fine-tuning:
-
-
-
-GRPO's novelty lies in its capacity to "directly optimize for preference rectification." This signifies a more direct and efficient route to aligning the model with desired outputs, contrasting with traditional Reinforcement Learning algorithms such as PPO. Let's break down how GRPO works through its three main components.
-
-### Group Formation: Creating Multiple Solutions
-
-The first step in GRPO is remarkably intuitive - it's similar to how a student might solve a difficult problem by trying multiple approaches. When given a prompt, the model doesn't just generate one response; instead, it creates multiple attempts at solving the same problem (usually 4, 8, or 16 different attempts).
-
-Imagine you're teaching a model to solve math problems. For a question about counting chickens on a farm, the model might generate several different solutions:
-- One solution might break down the problem step by step: first counting total chickens, then subtracting roosters, and finally accounting for non-laying hens
-- Another might use a different but equally valid approach
-- Some attempts might contain mistakes or less efficient solutions
-
-All these attempts are kept together as a group, much like having multiple students' solutions to compare and learn from.
-
-
-
-### Preference Learning: Understanding What Makes a Good Solution
-
-This is where GRPO really shines in its simplicity. Unlike other methods for RLHF that need always require a separate reward model to predict how good a solution might be, GRPO can use any function or model to evaluate the quality of a solution. For example, we could use a length function to reward shorter responses or a mathematical solver to reward accurate mathematical solutions.
-
-The evaluation process looks at various aspects of each solution:
-- Is the final answer correct?
-- Did the solution follow proper formatting (like using the right XML tags)?
-- Does the reasoning match the answer provided?
-
-What makes this approach particularly clever is how it handles the scoring. Instead of just giving absolute scores, GRPO normalizes the rewards within each group. It uses a simple but effective formula for group relative advantage estimation:
-
-```
-Advantage = (reward - mean(group_rewards)) / std(group_rewards)
-```
-
-
-
-This normalization is like grading on a curve, but for AI. It helps the model understand which solutions within the group were better or worse compared to their peers, rather than just looking at absolute scores.
-
-### Optimization: Learning from Experience
-
-The final step is where GRPO teaches the model to improve based on what it learned from evaluating the group of solutions. This process is both powerful and stable, using two main principles:
-
-1. It encourages the model to produce more solutions like the successful ones while moving away from less effective approaches
-2. It includes a safety mechanism (called KL divergence penalty) that prevents the model from changing too drastically all at once
-
-This approach proves more stable than traditional methods because:
-- It looks at multiple solutions together rather than comparing just two at a time
-- The group-based normalization helps prevent issues with reward scaling
-- The KL penalty acts like a safety net, ensuring the model doesn't forget what it already knows while learning new things
-
-> [!TIP]
-> GRPO's key innovations are:
-> - Learning directly from any function or model, eliminating the reliance on a separate reward model.
-> - Group-based learning, which is more stable and efficient than traditional methods like pairwise comparisons.
-
-This breakdown is complex, but the key takeaway is that GRPO is a more efficient and stable way to train a model to reason.
-
-### GRPO Algorithm in Pseudocode
-
-Now that we understand the key components of GRPO, let's look at the algorithm in pseudocode. This is a simplified version of the algorithm, but it captures the key ideas.
-
-```
-Input:
-- initial_policy: Starting model to be trained
-- reward_function: Function that evaluates outputs
-- training_prompts: Set of training examples
-- group_size: Number of outputs per prompt (typically 4-16)
-
-Algorithm GRPO:
-1. For each training iteration:
- a. Set reference_policy = initial_policy (snapshot current policy)
- b. For each prompt in batch:
- i. Generate group_size different outputs using initial_policy
- ii. Compute rewards for each output using reward_function
- iii. Normalize rewards within group:
- normalized_advantage = (reward - mean(rewards)) / std(rewards)
- iv. Update policy by maximizing the clipped ratio:
- min(prob_ratio * normalized_advantage,
- clip(prob_ratio, 1-epsilon, 1+epsilon) * normalized_advantage)
- - kl_weight * KL(initial_policy || reference_policy)
-
- where prob_ratio is current_prob / reference_prob
-
-Output: Optimized policy model
-```
-
-This algorithm shows how GRPO combines group-based advantage estimation with policy optimization while maintaining stability through clipping and KL divergence constraints.
-
-## Results and Impact
-
-Now that we've explored the algorithm, let's look at the results. DeepSeek R1 achieves state-of-the-art performance across multiple domains:
-
-| Domain | Key Results |
-|--------|-------------|
-| Mathematics | • 79.8% on AIME 2024
• 97.3% on MATH-500 |
-| Coding | • Codeforces Rating: 2029
• LiveCodeBench: 65.9% |
-| General Knowledge | • MMLU: 90.8%
• GPQA Diamond: 71.5% |
-| Language Tasks | • AlpacaEval 2.0: 87.6% win rate
• FRAMES: 82.5% |
-
-The model's practical impact extends beyond benchmarks through its cost-effective API pricing ($0.14 per million input tokens) and successful model distillation across various sizes (1.5B to 70B parameters). Notably, even the 7B model achieves 55.5% on AIME 2024, while the 70B distilled version approaches o1-mini performance on MATH-500 (94.5%), demonstrating effective capability preservation at different scales.
-
-## Limitations and Challenges of GRPO
-
-While GRPO represents a significant advancement in reinforcement learning for language models, it's important to understand its limitations and challenges:
-
-- **Generation Cost**: Generating multiple completions (4-16) for each prompt increases computational requirements compared to methods that generate only one or two completions.
-- **Batch Size Constraints**: The need to process groups of completions together can limit effective batch sizes, adding complexity to the training process and potentially slowing down training.
-- **Reward Function Design**: The quality of training heavily depends on well-designed reward functions. Poorly designed rewards can lead to unintended behaviors or optimization for the wrong objectives.
-- **Group Size Tradeoffs**: Choosing the optimal group size involves balancing diversity of solutions against computational cost. Too few samples may not provide enough diversity, while too many increase training time and resource requirements.
-- **KL Divergence Tuning**: Finding the right balance for the KL divergence penalty requires careful tuning - too high and the model won't learn effectively, too low and it may diverge too far from its initial capabilities.
-
-## Conclusion
-
-The DeepSeek R1 paper represents a significant milestone in language model development. The Group Relative Policy Optimization (GRPO) algorithm has demonstrated that pure reinforcement learning can indeed develop strong reasoning capabilities, challenging previous assumptions about the necessity of supervised fine-tuning.
-
-Perhaps most importantly, DeepSeek R1 has shown that it's possible to balance high performance with practical considerations like cost-effectiveness and accessibility. The successful distillation of the model's capabilities across different sizes, from 1.5B to 70B parameters, demonstrates a path forward for making advanced AI capabilities more widely available.
-
----
-
-In the next section, we'll explore practical implementations of these concepts, focusing on how to leverage GRPO and RFTrans in your own language model development projects.
-
-## Quiz
-
-### 1. What is the main innovation of the DeepSeek R1 paper?
-
-
-
-### 2. What are the four phases of the DeepSeek R1 training process?
-
-
-
-### 3. What is the 'Aha Moment' phenomenon in R1-Zero's training?
-
-
-
-### 4. How does GRPO's group formation work?
-
-
-
-### 5. What is the key difference between DeepSeek-R1-Zero and DeepSeek-R1?
-
-
-
+# Understanding the DeepSeek R1 Paper
+
+This chapter is a crash course paper reading. We will walk through the paper in simple terms, and then we will break down the key concepts and takeaways.
+
+DeepSeek R1 represents a significant advancement in language model training, particularly in developing reasoning capabilities through reinforcement learning. The paper introduces a new reinforcement learning algorithm called Group Relative Policy Optimization (GRPO).
+
+
+
+In the next chapter, we will build on this knowledge and implement GRPO in practice.
+
+The initial goal of the paper was to explore whether pure reinforcement learning could develop reasoning capabilities without supervised fine-tuning.
+
+> [!TIP]
+> Up until that point, all the popular LLMs required some supervised fine-tuning, which we explored in [chapter 11](/course/chapter11/1).
+
+## The Breakthrough 'Aha' Moment
+
+
+
+One of the most remarkable discoveries in R1-Zero's training was the emergence of a phenomenon known as the "Aha Moment." This phenomenon is somewhat similar to how humans experience sudden realizations while problem-solving. Here's how it works:
+
+1. Initial Attempt: The model makes an initial attempt at solving a problem
+2. Recognition: It recognizes potential errors or inconsistencies
+3. Self-Correction: It adjusts its approach based on this recognition
+4. Explanation: It can explain why the new approach is better
+
+This breakthrough resonates with learners and feels like a "Eureka" moment. It demonstrates learning rather than mere memorization, so let's take a moment to imagine what it feels like to have an "Aha" moment.
+
+For example, imagine you're trying to solve a puzzle:
+- First try: "This piece should go here based on the color"
+- Recognition: "But wait, the shape doesn't quite fit"
+- Correction: "Ah, it actually belongs over there"
+- Explanation: "Because both the color and shape pattern match in this position"
+
+This ability emerged naturally from RL training, without being explicitly programmed, demonstrating learning rather than mere memorization of a process from the training data.
+
+The easiest way to understand the 'Aha' moment is to see it in action. Let's take a look at an example. In the chat below, we ask the model to solve a problem and the UI shows the model's thought process as it solves the problem.
+
+
+
+If you want to try Deepseek's R1, you can also check out [Hugging Chat](https://huggingface.co/chat/models/deepseek-ai/DeepSeek-R1-Distill-Qwen-32B).
+
+## The Training Process
+
+Training R1 was a multi-phase process. Let's break down the phases and the key innovations in each phase.
+
+The final process results in two models:
+- DeepSeek-R1-Zero: A model trained purely using reinforcement learning.
+- DeepSeek-R1: A model that builds on the foundation of DeepSeek-R1-Zero and adds supervised fine-tuning.
+
+| Feature | DeepSeek-R1-Zero | DeepSeek-R1 |
+|---------|------------------|--------------|
+| Training Approach | Pure RL | Multi-phase (SFT + RL) |
+| Fine-tuning | None | Supervised fine-tuning |
+| Reasoning Capability | Emergent | Enhanced |
+| AIME Performance | 71.0% | 79.8% |
+| Key Characteristics | Strong reasoning but readability issues | Better language consistency and readability |
+
+While DeepSeek-R1-Zero demonstrates the potential of pure reinforcement learning for developing reasoning capabilities, DeepSeek-R1 builds upon this foundation with a more balanced approach that prioritizes both reasoning performance and usability.
+
+The training process involves four phases:
+
+1. Cold Start Phase
+2. Reasoning RL Phase
+3. Rejection Sampling Phase
+4. Diverse RL Phase
+
+Let's break down each phase:
+
+### Cold Start Phase (Quality Foundation)
+
+
+
+This phase is designed to establish a strong foundation for the model's readability and response quality. It uses a small dataset of high-quality samples from R1-Zero to fine-tune the V3-Base model. Starting with the DeepSeek-V3-Base model, the team used thousands of validated, high-quality samples from R1-Zero for supervised fine-tuning. This innovative approach uses a small but high quality dataset to establish strong baseline readability and response quality.
+
+### Reasoning RL Phase (Capability Building)
+
+
+
+The Reasoning RL Phase focuses on developing core reasoning capabilities across domains including mathematics, coding, science, and logic. This phase employs rule-based reinforcement learning, with rewards directly tied to solution correctness.
+
+Crucially, all the tasks in this phase are 'verifiable' so we can check if the model's answer is correct or not. For example, in the case of mathematics, we can check if the model's answer is correct by using a mathematical solver.
+
+What makes this phase particularly innovative is its direct optimization approach that eliminates the need for a separate reward model, streamlining the training process.
+
+### Rejection Sampling Phase (Quality Control)
+
+
+
+During the Rejection Sampling Phase, the model generates samples which are then filtered through a quality control process. DeepSeek-V3 serves as the quality judge, evaluating outputs across a broad scope that extends beyond pure reasoning tasks. The filtered data is then used for supervised fine-tuning. This phase's innovation lies in its ability to combine multiple quality signals to ensure high-standard outputs.
+
+### Diverse RL Phase (Broad Alignment)
+
+
+
+The final Diverse RL Phase tackles multiple task types using a sophisticated hybrid approach. For deterministic tasks, it employs rule-based rewards, while subjective tasks are evaluated through LLM feedback. This phase aims to achieve human preference alignment through its innovative hybrid reward approach, combining the precision of rule-based systems with the flexibility of language model evaluation.
+
+## The Algorithm: Group Relative Policy Optimization (GRPO)
+
+Now that we have a good understanding of the training process, let's look at the algorithm that was used to train the model.
+
+The authors describe GRPO as a breakthrough in model fine-tuning:
+
+
+
+GRPO's novelty lies in its capacity to "directly optimize for preference rectification." This signifies a more direct and efficient route to aligning the model with desired outputs, contrasting with traditional Reinforcement Learning algorithms such as PPO. Let's break down how GRPO works through its three main components.
+
+### Group Formation: Creating Multiple Solutions
+
+The first step in GRPO is remarkably intuitive - it's similar to how a student might solve a difficult problem by trying multiple approaches. When given a prompt, the model doesn't just generate one response; instead, it creates multiple attempts at solving the same problem (usually 4, 8, or 16 different attempts).
+
+Imagine you're teaching a model to solve math problems. For a question about counting chickens on a farm, the model might generate several different solutions:
+- One solution might break down the problem step by step: first counting total chickens, then subtracting roosters, and finally accounting for non-laying hens
+- Another might use a different but equally valid approach
+- Some attempts might contain mistakes or less efficient solutions
+
+All these attempts are kept together as a group, much like having multiple students' solutions to compare and learn from.
+
+
+
+### Preference Learning: Understanding What Makes a Good Solution
+
+This is where GRPO really shines in its simplicity. Unlike other methods for RLHF that need always require a separate reward model to predict how good a solution might be, GRPO can use any function or model to evaluate the quality of a solution. For example, we could use a length function to reward shorter responses or a mathematical solver to reward accurate mathematical solutions.
+
+The evaluation process looks at various aspects of each solution:
+- Is the final answer correct?
+- Did the solution follow proper formatting (like using the right XML tags)?
+- Does the reasoning match the answer provided?
+
+What makes this approach particularly clever is how it handles the scoring. Instead of just giving absolute scores, GRPO normalizes the rewards within each group. It uses a simple but effective formula for group relative advantage estimation:
+
+```
+Advantage = (reward - mean(group_rewards)) / std(group_rewards)
+```
+
+
+
+This normalization is like grading on a curve, but for AI. It helps the model understand which solutions within the group were better or worse compared to their peers, rather than just looking at absolute scores.
+
+### Optimization: Learning from Experience
+
+The final step is where GRPO teaches the model to improve based on what it learned from evaluating the group of solutions. This process is both powerful and stable, using two main principles:
+
+1. It encourages the model to produce more solutions like the successful ones while moving away from less effective approaches
+2. It includes a safety mechanism (called KL divergence penalty) that prevents the model from changing too drastically all at once
+
+This approach proves more stable than traditional methods because:
+- It looks at multiple solutions together rather than comparing just two at a time
+- The group-based normalization helps prevent issues with reward scaling
+- The KL penalty acts like a safety net, ensuring the model doesn't forget what it already knows while learning new things
+
+> [!TIP]
+> GRPO's key innovations are:
+> - Learning directly from any function or model, eliminating the reliance on a separate reward model.
+> - Group-based learning, which is more stable and efficient than traditional methods like pairwise comparisons.
+
+This breakdown is complex, but the key takeaway is that GRPO is a more efficient and stable way to train a model to reason.
+
+### GRPO Algorithm in Pseudocode
+
+Now that we understand the key components of GRPO, let's look at the algorithm in pseudocode. This is a simplified version of the algorithm, but it captures the key ideas.
+
+```
+Input:
+- initial_policy: Starting model to be trained
+- reward_function: Function that evaluates outputs
+- training_prompts: Set of training examples
+- group_size: Number of outputs per prompt (typically 4-16)
+
+Algorithm GRPO:
+1. For each training iteration:
+ a. Set reference_policy = initial_policy (snapshot current policy)
+ b. For each prompt in batch:
+ i. Generate group_size different outputs using initial_policy
+ ii. Compute rewards for each output using reward_function
+ iii. Normalize rewards within group:
+ normalized_advantage = (reward - mean(rewards)) / std(rewards)
+ iv. Update policy by maximizing the clipped ratio:
+ min(prob_ratio * normalized_advantage,
+ clip(prob_ratio, 1-epsilon, 1+epsilon) * normalized_advantage)
+ - kl_weight * KL(initial_policy || reference_policy)
+
+ where prob_ratio is current_prob / reference_prob
+
+Output: Optimized policy model
+```
+
+This algorithm shows how GRPO combines group-based advantage estimation with policy optimization while maintaining stability through clipping and KL divergence constraints.
+
+## Results and Impact
+
+Now that we've explored the algorithm, let's look at the results. DeepSeek R1 achieves state-of-the-art performance across multiple domains:
+
+| Domain | Key Results |
+|--------|-------------|
+| Mathematics | • 79.8% on AIME 2024
• 97.3% on MATH-500 |
+| Coding | • Codeforces Rating: 2029
• LiveCodeBench: 65.9% |
+| General Knowledge | • MMLU: 90.8%
• GPQA Diamond: 71.5% |
+| Language Tasks | • AlpacaEval 2.0: 87.6% win rate
• FRAMES: 82.5% |
+
+The model's practical impact extends beyond benchmarks through its cost-effective API pricing ($0.14 per million input tokens) and successful model distillation across various sizes (1.5B to 70B parameters). Notably, even the 7B model achieves 55.5% on AIME 2024, while the 70B distilled version approaches o1-mini performance on MATH-500 (94.5%), demonstrating effective capability preservation at different scales.
+
+## Limitations and Challenges of GRPO
+
+While GRPO represents a significant advancement in reinforcement learning for language models, it's important to understand its limitations and challenges:
+
+- **Generation Cost**: Generating multiple completions (4-16) for each prompt increases computational requirements compared to methods that generate only one or two completions.
+- **Batch Size Constraints**: The need to process groups of completions together can limit effective batch sizes, adding complexity to the training process and potentially slowing down training.
+- **Reward Function Design**: The quality of training heavily depends on well-designed reward functions. Poorly designed rewards can lead to unintended behaviors or optimization for the wrong objectives.
+- **Group Size Tradeoffs**: Choosing the optimal group size involves balancing diversity of solutions against computational cost. Too few samples may not provide enough diversity, while too many increase training time and resource requirements.
+- **KL Divergence Tuning**: Finding the right balance for the KL divergence penalty requires careful tuning - too high and the model won't learn effectively, too low and it may diverge too far from its initial capabilities.
+
+## Conclusion
+
+The DeepSeek R1 paper represents a significant milestone in language model development. The Group Relative Policy Optimization (GRPO) algorithm has demonstrated that pure reinforcement learning can indeed develop strong reasoning capabilities, challenging previous assumptions about the necessity of supervised fine-tuning.
+
+Perhaps most importantly, DeepSeek R1 has shown that it's possible to balance high performance with practical considerations like cost-effectiveness and accessibility. The successful distillation of the model's capabilities across different sizes, from 1.5B to 70B parameters, demonstrates a path forward for making advanced AI capabilities more widely available.
+
+---
+
+In the next section, we'll explore practical implementations of these concepts, focusing on how to leverage GRPO and RFTrans in your own language model development projects.
+
+## Quiz
+
+### 1. What is the main innovation of the DeepSeek R1 paper?
+
+
+
+### 2. What are the four phases of the DeepSeek R1 training process?
+
+
+
+### 3. What is the 'Aha Moment' phenomenon in R1-Zero's training?
+
+
+
+### 4. How does GRPO's group formation work?
+
+
+
+### 5. What is the key difference between DeepSeek-R1-Zero and DeepSeek-R1?
+
+
+
diff --git a/chapters/en/chapter12/3b.mdx b/chapters/en/chapter12/3b.mdx
index a849c0b9d..79ce03a41 100644
--- a/chapters/en/chapter12/3b.mdx
+++ b/chapters/en/chapter12/3b.mdx
@@ -1,400 +1,400 @@
-# Advanced Understanding of Group Relative Policy Optimization (GRPO) in DeepSeekMath
-
-> [!TIP]
-> This section dives into the technical and mathematical details of GRPO. It was authored by Shirin Yamani.
-
-Let's deepen our understanding of GRPO so that we can improve our model's training process.
-
-GRPO directly evaluates the model-generated responses by comparing them within groups of generation to optimize policy model, instead of training a separate value model (Critic). This approach leads to significant reduction in computational cost!
-
-GRPO can be applied to any verifiable task where the correctness of the response can be determined. For instance, in math reasoning, the correctness of the response can be easily verified by comparing it to the ground truth.
-
-Before diving into the technical details, let's visualize how GRPO works at a high level:
-
-
-
-Now that we have a visual overview, let's break down how GRPO works step by step.
-
-## The GRPO Algorithm
-
-The core innovation of GRPO is its approach to evaluating and learning from multiple generated responses simultaneously. Instead of relying on a separate reward model, it compares outputs within the same group to determine which ones should be reinforced.
-
-Let's walk through each step of the algorithm in detail:
-
-### Step 1: Group Sampling
-
-The first step is to generate multiple possible answers for each question. This creates a diverse set of outputs that can be compared against each other.
-
-For each question \\( q \\), the model will generate \\( G \\) outputs (group size) from the trained policy: { \\( {o_1, o_2, o_3, \dots, o_G}\pi_{\theta_{\text{old}}} \\) }, \\( G=8 \\) where each \\( o_i \\) represents one completion from the model.
-
-#### Example
-
-To make this concrete, let's look at a simple arithmetic problem:
-
-**Question**
-
-\\( q \\) : \\( \text{Calculate}\space2 + 2 \times 6 \\)
-
-**Outputs**
-
-\\( (G = 8) \\): \\( \{o_1:14 \text{ (correct)}, o_2:16 \text{ (wrong)}, o_3:10 \text{ (wrong)}, \ldots, o_8:14 \text{ (correct)}\} \\)
-
-Notice how some of the generated answers are correct (14) while others are wrong (16 or 10). This diversity is crucial for the next step.
-
-### Step 2: Advantage Calculation
-
-Once we have multiple responses, we need a way to determine which ones are better than others. This is where the advantage calculation comes in.
-
-#### Reward Distribution
-
-First, we assign a reward score to each generated response. In this example, we'll use a reward model, but as we learnt in the previous section, we can use any reward returning function.
-
-Assign a RM score to each of the generated responses based on the correctness \\( r_i \\) *(e.g. 1 for correct response, 0 for wrong response)* then for each of the \\( r_i \\) calculate the following Advantage value.
-
-#### Advantage Value Formula
-
-The key insight of GRPO is that we don't need absolute measures of quality - we can compare outputs within the same group. This is done using standardization:
-
-$$A_i = \frac{r_i - \text{mean}(\{r_1, r_2, \ldots, r_G\})}{\text{std}(\{r_1, r_2, \ldots, r_G\})}$$
-
-#### Example
-
-Continuing with our arithmetic example for the same example above, imagine we have 8 responses, 4 of which is correct and the rest wrong, therefore;
-
-| Metric | Value |
-|--------|-------|
-| Group Average | \\( mean(r_i) = 0.5 \\) |
-| Standard Deviation | \\( std(r_i) = 0.53 \\) |
-| Advantage Value (Correct response) | \\( A_i = \frac{1 - 0.5}{0.53}= 0.94 \\) |
-| Advantage Value (Wrong response) | \\( A_i = \frac{0 - 0.5}{0.53}= -0.94 \\) |
-
-#### Interpretation
-
-Now that we have calculated the advantage values, let's understand what they mean:
-
-This standardization (i.e. \\( A_i \\) weighting) allows the model to assess each response's relative performance, guiding the optimization process to favorable responses that are better than average (high reward) and discourage those that are worse. For instance if \\( A_i > 0 \\), then the \\( o_i \\) is better response than the average level within its group; and if \\( A_i < 0 \\), then the \\( o_i \\) then the quality of the response is less than the average (i.e. poor quality/performance).
-
-For the example above, if \\( A_i = 0.94 \text{(correct output)} \\) then during optimization steps its generation probability will be increased.
-
-With our advantage values calculated, we're now ready to update the policy.
-
-### Step 3: Policy Update
-
-The final step is to use these advantage values to update our model so that it becomes more likely to generate good responses in the future.
-
-The target function for policy update is:
-
-$$J_{GRPO}(\theta) = \left[\frac{1}{G} \sum_{i=1}^{G} \min \left( \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} A_i \text{clip}\left( \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}, 1 - \epsilon, 1 + \epsilon \right) A_i \right)\right]- \beta D_{KL}(\pi_{\theta} \|\| \pi_{ref})$$
-
-This formula might look intimidating at first, but it's built from several components that each serve an important purpose. Let's break them down one by one.
-
-## Key Components of the Target Function
-
-The GRPO update function combines several techniques to ensure stable and effective learning. Let's examine each component:
-
-### 1. Probability Ratio
-
-The probability ratio is defined as:
-
-\\( \left(\frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}\right) \\)
-
-Intuitively, the formula compares how much the new model's response probability differs from the old model's response probability while incorporating a preference for responses that improve the expected outcome.
-
-#### Interpretation
-
-- If \\( \text{ratio} > 1 \\), the new model assigns a higher probability to response \\( o_i \\) than the old model.
-- If \\( \text{ratio} < 1 \\), the new model assigns a lower probability to \\( o_i \\)
-
-This ratio allows us to control how much the model changes at each step, which leads us to the next component.
-
-### 2. Clip Function
-
-The clipping function is defined as:
-
-\\( \text{clip}\left( \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}, 1 - \epsilon, 1 + \epsilon\right) \\)
-
-Limit the ratio discussed above to be within \\( [1 - \epsilon, 1 + \epsilon] \\) to avoid/control drastic changes or crazy updates and stepping too far off from the old policy. In other words, it limit how much the probability ratio can increase to help maintaining stability by avoiding updates that push the new model too far from the old one.
-
-#### Example (ε = 0.2)
-
-Let's look at two different scenarios to better understand this clipping function:
-
-- **Case 1**: if the new policy has a probability of 0.9 for a specific response and the old policy has a probabiliy of 0.5, it means this response is getting reinforeced by the new policy to have higher probability, but within a controlled limit which is the clipping to tight up its hands to not get drastic
- - \\( \text{Ratio}: \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} = \frac{0.9}{0.5} = 1.8 → \text{Clip}\space1.2 \\) (upper bound limit 1.2)
-- **Case 2**: If the new policy is not in favour of a response (lower probability e.g. 0.2), meaning if the response is not beneficial the increase might be incorrect, and the model would be penalized.
- - \\( \text{Ratio}: \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} = \frac{0.2}{0.5} = 0.4 →\text{Clip}\space0.8 \\) (lower bound limit 0.8)
-
-#### Interpretation
-
-- The formula encourages the new model to favour responses that the old model underweighted **if they improve the outcome**.
-- If the old model already favoured a response with a high probability, the new model can still reinforce it **but only within a controlled limit \\( [1 - \epsilon, 1 + \epsilon] \\), \\( \text{(e.g., }\epsilon = 0.2, \space \text{so} \space [0.8-1.2]) \\)**.
-- If the old model overestimated a response that performs poorly, the new model is **discouraged** from maintaining that high probability.
-- Therefore, intuitively, By incorporating the probability ratio, the objective function ensures that updates to the policy are proportional to the advantage \\( A_i \\) while being moderated to prevent drastic changes. T
-
-While the clipping function helps prevent drastic changes, we need one more safeguard to ensure our model doesn't deviate too far from its original behavior.
-
-### 3. KL Divergence
-
-The KL divergence term is:
-
-\\( \beta D_{KL}(\pi_{\theta} \|\| \pi_{ref}) \\)
-
-In the KL divergence term, the \\( \pi_{ref} \\) is basically the pre-update model's output, `per_token_logps` and \\( \pi_{\theta} \\) is the new model's output, `new_per_token_logps`. Theoretically, KL divergence is minimized to prevent the model from deviating too far from its original behavior during optimization. This helps strike a balance between improving performance based on the reward signal and maintaining coherence. In this context, minimizing KL divergence reduces the risk of the model generating nonsensical text or, in the case of mathematical reasoning, producing extremely incorrect answers.
-
-#### Interpretation
-
-- A KL divergence penalty keeps the model's outputs close to its original distribution, preventing extreme shifts.
-- Instead of drifting towards completely irrational outputs, the model would refine its understanding while still allowing some exploration
-
-#### Math Definition
-
-For those interested in the mathematical details, let's look at the formal definition:
-
-Recall that KL distance is defined as follows:
-$$D_{KL}(P \|\| Q) = \sum_{x \in X} P(x) \log \frac{P(x)}{Q(x)}$$
-In RLHF, the two distributions of interest are often the distribution of the new model version, P(x), and a distribution of the reference policy, Q(x).
-
-#### The Role of β Parameter
-
-The coefficient \\( \beta \\) controls how strongly we enforce the KL divergence constraint:
-
-- **Higher β (Stronger KL Penalty)**
- - More constraint on policy updates. The model remains close to its reference distribution.
- - Can slow down adaptation: The model may struggle to explore better responses.
-- **Lower β (Weaker KL Penalty)**
- - More freedom to update policy: The model can deviate more from the reference.
- - Faster adaptation but risk of instability: The model might learn reward-hacking behaviors.
- - Over-optimization risk: If the reward model is flawed, the policy might generate nonsensical outputs.
-- **Original** [DeepSeekMath](https://arxiv.org/abs/2402.03300) paper set this \\( \beta= 0.04 \\)
-
-Now that we understand the components of GRPO, let's see how they work together in a complete example.
-
-## Worked Example with GRPO
-
-To solidify our understanding of GRPO, let's walk through a complete example from start to finish.
-
-### Example Problem
-
-$$\text{Q: Calculate}\space2 + 2 \times 6$$
-
-### Step 1: Group Sampling
-
-First, we generate multiple responses from our model.
-
-Generate \\( (G = 8) \\) responses, \\( 4 \\) of which are correct answer (\\( 14, \text{reward=} 1 \\)) and \\( 4 \\) incorrect \\( \text{(reward= 0)} \\), Therefore:
-
-$${o_1:14(correct), o_2:10 (wrong), o_3:16 (wrong), ... o_G:14(correct)}$$
-
-### Step 2: Advantage Calculation
-
-Next, we calculate the advantage values to determine which responses are better than average:
-
-| Statistic | Value |
-|-----------|-------|
-| Group Average | \\( mean(r_i) = 0.5 \\) |
-| Standard Deviation | \\( std(r_i) = 0.53 \\) |
-| Advantage Value (Correct response) | \\( A_i = \frac{1 - 0.5}{0.53}= 0.94 \\) |
-| Advantage Value (Wrong response) | \\( A_i = \frac{0 - 0.5}{0.53}= -0.94 \\) |
-
-### Step 3: Policy Update
-
-Finally, we update our model to reinforce the correct responses:
-
-- Assuming the probability of old policy (\\( \pi_{\theta_{old}} \\)) for a correct output \\( o_1 \\) is \\( 0.5 \\) and the new policy increases it to \\( 0.7 \\) then:
-$$\text{Ratio}: \frac{0.7}{0.5} = 1.4 →\text{after Clip}\space1.2 \space (\epsilon = 0.2)$$
-- Then when the target function is re-weighted, the model tends to reinforce the generation of correct output, and the \\( \text{KL Divergence} \\) limits the deviation from the reference policy.
-
-With the theoretical understanding in place, let's see how GRPO can be implemented in code.
-
-## Implementation Example
-
-Let's put everything together in a practical example. The following code demonstrates how to implement GRPO in PyTorch.
-
-### 1. Loading the Model and Generating Responses
-
-First, we need to load a model and generate multiple responses for a given question:
-
-```python
-import torch
-import torch.nn.functional as F
-from transformers import AutoModelForCausalLM, AutoTokenizer
-
-# Load the model and tokenizer
-model_name = "Qwen/Qwen2-Math-1.5B"
-model = AutoModelForCausalLM.from_pretrained(model_name)
-tokenizer = AutoTokenizer.from_pretrained(model_name)
-model.eval()
-
-# Move model to GPU if available
-device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
-model.to(device)
-
-# Input prompt
-prompt = "Solve y = 2x + 1 for x = 2, y = " # Correct answer: 5
-inputs = tokenizer(prompt, return_tensors="pt", padding=True)
-input_ids = inputs["input_ids"].to(device) # Shape: (1, prompt_len)
-attention_mask = inputs["attention_mask"].to(device)
-
-# Step 1: Generate 8 responses (B = 2 groups, G = 4 responses per group)
-batch_size, num_generations = 2, 4
-outputs = model.generate(
- input_ids=input_ids, # Shape: (1, prompt_len)
- attention_mask=attention_mask,
- max_new_tokens=1, # seq_len = 1 (single token per response)
- num_return_sequences=batch_size * num_generations, # 8 responses total
- do_sample=True,
- top_k=10,
- temperature=0.7,
- pad_token_id=tokenizer.eos_token_id,
- return_dict_in_generate=True,
- output_scores=True,
-)
-```
-
-this initial Generation (Before Any Steps) will output sth like this:
-
-```text
-Output 1: 5.0
-Output 2: 6.0
-Output 3: 7.0
-Output 4: 5.0
-Output 5: 10.0
-Output 6: 2.0
-Output 7: 5.0
-Output 8: 5.0
-```
-
-### 2. Calculating Rewards
-
-Now, we need to determine which responses are correct and assign rewards accordingly:
-
-With GRPO, with the same sample prompt, we generate multiple completions. So for instance, for our prompts of `"Solve y = 2x + 1 for x = 2, y = "` and `Solve y = 2x + 1 for x = 4, y = "` we have two group of generated outputs for the given prompt one is say
-- `[5, 6, 7, 5]` and the other is
-- `[10, 2, 9, 9]` while the correct answer is 5 and 9.
-
-Note that in practice these reward scores are achieved by a rule-based reward function that assigns rewards based on the correctness of the response or a more complex neural network-based model that can be trained to assign rewards based on the correctness of the response or a mixed of both. But for sake of simplicity let's say our reward per response is 1 if the response is correct and 0 if it is wrong, therefore;
-```python
-reward_1 = [1, 0, 0, 1]
-reward_2 = [0, 0, 1, 1]
-```
-next we get the group_wise mean and std of the rewards;
-
-```python
-# Shape: (B * G,) = (8,) bc we have 2 groups of 4 generations that we flatten
-rewards = torch.tensor([1, 0, 0, 1, 0, 0, 1, 1], dtype=torch.float32)
-num_generations = 4
-
-# Group rewards: Shape (B, G) = 2, 4)
-rewards_grouped = rewards.view(-1, num_generations)
-
-# Mean per group: Shape (B,) = (2,)
-mean_grouped_rewards = rewards_grouped.mean(dim=1)
-
-# Std per group: Shape (B,) = (2,)
-std_grouped_rewards = rewards_grouped.std(dim=1)
-
-# Broadcast to match rewards and normalize: Shape (B * G,) = (8,)
-# why we need to broadcast? because we need to calculate the advantage values for each response within the group
-mean_grouped_rewards = mean_grouped_rewards.repeat_interleave(num_generations, dim=0)
-std_grouped_rewards = std_grouped_rewards.repeat_interleave(num_generations, dim=0)
-```
-this will output:
-```text
-Grouped Rewards: tensor([[1., 0., 0., 1.],
- [0., 0., 1., 1.]])
-Mean per group: tensor([0.5000, 0.5000])
-Std per group: tensor([0.5774, 0.5774])
-Broadcasted Mean: tensor([0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000])
-Broadcasted Std: tensor([0.5774, 0.5774, 0.5774, 0.5774, 0.5774, 0.5774, 0.5774, 0.5774])
-```
-Now we can calculate the advantage values for each response:
-```python
-# Advantages: Shape (B * G,) = (8,)
-advantages = (rewards - mean_grouped_rewards) / (std_grouped_rewards + 1e-8)
-```
-this will output:
-```text
-Advantages: tensor([ 0.8659, -0.8660, -0.8660, 0.8659, -0.8660, -0.8660, 0.8659, 0.8659])
-```
-which is coming from the Advantage formula above, so:
-```text
-For reward_1 = [1, 0, 0, 1]:
-1 - 0.5 / 0.5774 ≈ 0.8659
-0 - 0.5 / 0.5774 ≈ -0.8660
-For reward_2 = [0, 0, 1, 1]: Same pattern.
-```
-however, the shape here is `(B*G,) = (8,)` but in practice, we need to have the shape of `(B, G) = (2, 4)` to match the logits shape, right? Therefore, we need to unsqueeze the advantages tensor to have the shape of `(B*G, 1) = (8, 1)` to match the logits shape.
-```python
-# Shape (B * G, 1) = (8, 1) to match the logits shape
-advantages = advantages.unsqueeze(1)
-```
-which will output:
-```text
-Advantages: tensor([[ 0.8659],
- [-0.8660],
- [-0.8660],
- [ 0.8659],
- [-0.8660],
- [-0.8660],
- [ 0.8659],
- [ 0.8659]])
-```
-now we are good, let's move to the next step of updating the policy model based on the advantage values.
-
-### 3. Updating the Policy
-Finally, we use the advantage values to update our model:
-
-```python
-# Compute probability ratio between new and old policies
-ratio = torch.exp(
- new_per_token_logps - per_token_logps
-) # Shape: (B*G, seq_len) seq_len is the length of the output i.e. the num of generated tokens so here for simplicity let's assume it is 1 # (8, 1)
-```
-
-Note that the `per_token_logps` can be achieved by passing the generated outputs to the model and get the logits and then apply the softmax function to get the probabilities `F.softmax(logits, dim=-1)`.
-
-```python
-# Clipping Function
-eps = self.cliprange # e.g. 0.2
-pg_losses1 = -advantages * ratio # Shape: (B*G, seq_len) #(8, 1)
-pg_losses2 = -advantages * torch.clamp(
- ratio, 1.0 - eps, 1.0 + eps
-) # Shape: (B*G, seq_len) #(8, 1)
-pg_loss_max = torch.max(pg_losses1, pg_losses2) # Shape: (B*G, seq_len) #(8, 1)
-
-
-# Now Combine with KL penalty # Shape: (B*G, seq_len) #(8, 1)
-per_token_loss = pg_loss_max + self.beta * per_token_kl
-```
-
-`per_token_kl` can also be calculated as follows:
-
-```python
-# Shape: (B*G, seq_len) #(8, 1)
-per_token_kl = F.kl_div(
- F.log_softmax(new_per_token_logps, dim=-1),
- F.softmax(per_token_logps, dim=-1),
- reduction="none",
-).sum(dim=-1, keepdim=True)
-```
-
-Complete example can be found [here](./basic_example.py). GRPO is also implemented by the excellent TRL team, you can check the implementation [TRL/GRPO_trainer](https://github.com/huggingface/trl/blob/main/trl/trainer/grpo_trainer.py) for more details.
-
-## Summary and Next Steps
-
-Congratulations! You've now learned about Group Relative Policy Optimization (GRPO). To recap what we've covered:
-
-1. GRPO compares multiple outputs within a group to determine which ones are better than others, without requiring a separate value model.
-2. The advantage calculation standardizes rewards to identify which responses are above or below average.
-3. The policy update uses a clipped objective function with a KL divergence penalty to ensure stable learning.
-
-This approach is particularly powerful for mathematical reasoning tasks, where correctness can be objectively verified. The GRPO method allows for more efficient training compared to traditional RLHF approaches that require a separate critic model.
-
-As you continue exploring GRPO, consider experimenting with different group sizes, reward functions, and KL penalty coefficients to see how they affect your model's performance.
-
-Happy training! 🚀
-
-## References
-1. [RLHF Book by Nathan Lambert](https://github.com/natolambert/rlhf-book)
-2. [DeepSeek-V3 Technical Report](https://huggingface.co/papers/2412.19437)
-3. [DeepSeekMath](https://huggingface.co/papers/2402.03300)
+# Advanced Understanding of Group Relative Policy Optimization (GRPO) in DeepSeekMath
+
+> [!TIP]
+> This section dives into the technical and mathematical details of GRPO. It was authored by Shirin Yamani.
+
+Let's deepen our understanding of GRPO so that we can improve our model's training process.
+
+GRPO directly evaluates the model-generated responses by comparing them within groups of generation to optimize policy model, instead of training a separate value model (Critic). This approach leads to significant reduction in computational cost!
+
+GRPO can be applied to any verifiable task where the correctness of the response can be determined. For instance, in math reasoning, the correctness of the response can be easily verified by comparing it to the ground truth.
+
+Before diving into the technical details, let's visualize how GRPO works at a high level:
+
+
+
+Now that we have a visual overview, let's break down how GRPO works step by step.
+
+## The GRPO Algorithm
+
+The core innovation of GRPO is its approach to evaluating and learning from multiple generated responses simultaneously. Instead of relying on a separate reward model, it compares outputs within the same group to determine which ones should be reinforced.
+
+Let's walk through each step of the algorithm in detail:
+
+### Step 1: Group Sampling
+
+The first step is to generate multiple possible answers for each question. This creates a diverse set of outputs that can be compared against each other.
+
+For each question \\( q \\), the model will generate \\( G \\) outputs (group size) from the trained policy: { \\( {o_1, o_2, o_3, \dots, o_G}\pi_{\theta_{\text{old}}} \\) }, \\( G=8 \\) where each \\( o_i \\) represents one completion from the model.
+
+#### Example
+
+To make this concrete, let's look at a simple arithmetic problem:
+
+**Question**
+
+\\( q \\) : \\( \text{Calculate}\space2 + 2 \times 6 \\)
+
+**Outputs**
+
+\\( (G = 8) \\): \\( \{o_1:14 \text{ (correct)}, o_2:16 \text{ (wrong)}, o_3:10 \text{ (wrong)}, \ldots, o_8:14 \text{ (correct)}\} \\)
+
+Notice how some of the generated answers are correct (14) while others are wrong (16 or 10). This diversity is crucial for the next step.
+
+### Step 2: Advantage Calculation
+
+Once we have multiple responses, we need a way to determine which ones are better than others. This is where the advantage calculation comes in.
+
+#### Reward Distribution
+
+First, we assign a reward score to each generated response. In this example, we'll use a reward model, but as we learnt in the previous section, we can use any reward returning function.
+
+Assign a RM score to each of the generated responses based on the correctness \\( r_i \\) *(e.g. 1 for correct response, 0 for wrong response)* then for each of the \\( r_i \\) calculate the following Advantage value.
+
+#### Advantage Value Formula
+
+The key insight of GRPO is that we don't need absolute measures of quality - we can compare outputs within the same group. This is done using standardization:
+
+$$A_i = \frac{r_i - \text{mean}(\{r_1, r_2, \ldots, r_G\})}{\text{std}(\{r_1, r_2, \ldots, r_G\})}$$
+
+#### Example
+
+Continuing with our arithmetic example for the same example above, imagine we have 8 responses, 4 of which is correct and the rest wrong, therefore;
+
+| Metric | Value |
+|--------|-------|
+| Group Average | \\( mean(r_i) = 0.5 \\) |
+| Standard Deviation | \\( std(r_i) = 0.53 \\) |
+| Advantage Value (Correct response) | \\( A_i = \frac{1 - 0.5}{0.53}= 0.94 \\) |
+| Advantage Value (Wrong response) | \\( A_i = \frac{0 - 0.5}{0.53}= -0.94 \\) |
+
+#### Interpretation
+
+Now that we have calculated the advantage values, let's understand what they mean:
+
+This standardization (i.e. \\( A_i \\) weighting) allows the model to assess each response's relative performance, guiding the optimization process to favorable responses that are better than average (high reward) and discourage those that are worse. For instance if \\( A_i > 0 \\), then the \\( o_i \\) is better response than the average level within its group; and if \\( A_i < 0 \\), then the \\( o_i \\) then the quality of the response is less than the average (i.e. poor quality/performance).
+
+For the example above, if \\( A_i = 0.94 \text{(correct output)} \\) then during optimization steps its generation probability will be increased.
+
+With our advantage values calculated, we're now ready to update the policy.
+
+### Step 3: Policy Update
+
+The final step is to use these advantage values to update our model so that it becomes more likely to generate good responses in the future.
+
+The target function for policy update is:
+
+$$J_{GRPO}(\theta) = \left[\frac{1}{G} \sum_{i=1}^{G} \min \left( \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} A_i \text{clip}\left( \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}, 1 - \epsilon, 1 + \epsilon \right) A_i \right)\right]- \beta D_{KL}(\pi_{\theta} \|\| \pi_{ref})$$
+
+This formula might look intimidating at first, but it's built from several components that each serve an important purpose. Let's break them down one by one.
+
+## Key Components of the Target Function
+
+The GRPO update function combines several techniques to ensure stable and effective learning. Let's examine each component:
+
+### 1. Probability Ratio
+
+The probability ratio is defined as:
+
+\\( \left(\frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}\right) \\)
+
+Intuitively, the formula compares how much the new model's response probability differs from the old model's response probability while incorporating a preference for responses that improve the expected outcome.
+
+#### Interpretation
+
+- If \\( \text{ratio} > 1 \\), the new model assigns a higher probability to response \\( o_i \\) than the old model.
+- If \\( \text{ratio} < 1 \\), the new model assigns a lower probability to \\( o_i \\)
+
+This ratio allows us to control how much the model changes at each step, which leads us to the next component.
+
+### 2. Clip Function
+
+The clipping function is defined as:
+
+\\( \text{clip}\left( \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)}, 1 - \epsilon, 1 + \epsilon\right) \\)
+
+Limit the ratio discussed above to be within \\( [1 - \epsilon, 1 + \epsilon] \\) to avoid/control drastic changes or crazy updates and stepping too far off from the old policy. In other words, it limit how much the probability ratio can increase to help maintaining stability by avoiding updates that push the new model too far from the old one.
+
+#### Example (ε = 0.2)
+
+Let's look at two different scenarios to better understand this clipping function:
+
+- **Case 1**: if the new policy has a probability of 0.9 for a specific response and the old policy has a probabiliy of 0.5, it means this response is getting reinforeced by the new policy to have higher probability, but within a controlled limit which is the clipping to tight up its hands to not get drastic
+ - \\( \text{Ratio}: \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} = \frac{0.9}{0.5} = 1.8 → \text{Clip}\space1.2 \\) (upper bound limit 1.2)
+- **Case 2**: If the new policy is not in favour of a response (lower probability e.g. 0.2), meaning if the response is not beneficial the increase might be incorrect, and the model would be penalized.
+ - \\( \text{Ratio}: \frac{\pi_{\theta}(o_i|q)}{\pi_{\theta_{old}}(o_i|q)} = \frac{0.2}{0.5} = 0.4 →\text{Clip}\space0.8 \\) (lower bound limit 0.8)
+
+#### Interpretation
+
+- The formula encourages the new model to favour responses that the old model underweighted **if they improve the outcome**.
+- If the old model already favoured a response with a high probability, the new model can still reinforce it **but only within a controlled limit \\( [1 - \epsilon, 1 + \epsilon] \\), \\( \text{(e.g., }\epsilon = 0.2, \space \text{so} \space [0.8-1.2]) \\)**.
+- If the old model overestimated a response that performs poorly, the new model is **discouraged** from maintaining that high probability.
+- Therefore, intuitively, By incorporating the probability ratio, the objective function ensures that updates to the policy are proportional to the advantage \\( A_i \\) while being moderated to prevent drastic changes. T
+
+While the clipping function helps prevent drastic changes, we need one more safeguard to ensure our model doesn't deviate too far from its original behavior.
+
+### 3. KL Divergence
+
+The KL divergence term is:
+
+\\( \beta D_{KL}(\pi_{\theta} \|\| \pi_{ref}) \\)
+
+In the KL divergence term, the \\( \pi_{ref} \\) is basically the pre-update model's output, `per_token_logps` and \\( \pi_{\theta} \\) is the new model's output, `new_per_token_logps`. Theoretically, KL divergence is minimized to prevent the model from deviating too far from its original behavior during optimization. This helps strike a balance between improving performance based on the reward signal and maintaining coherence. In this context, minimizing KL divergence reduces the risk of the model generating nonsensical text or, in the case of mathematical reasoning, producing extremely incorrect answers.
+
+#### Interpretation
+
+- A KL divergence penalty keeps the model's outputs close to its original distribution, preventing extreme shifts.
+- Instead of drifting towards completely irrational outputs, the model would refine its understanding while still allowing some exploration
+
+#### Math Definition
+
+For those interested in the mathematical details, let's look at the formal definition:
+
+Recall that KL distance is defined as follows:
+$$D_{KL}(P \|\| Q) = \sum_{x \in X} P(x) \log \frac{P(x)}{Q(x)}$$
+In RLHF, the two distributions of interest are often the distribution of the new model version, P(x), and a distribution of the reference policy, Q(x).
+
+#### The Role of β Parameter
+
+The coefficient \\( \beta \\) controls how strongly we enforce the KL divergence constraint:
+
+- **Higher β (Stronger KL Penalty)**
+ - More constraint on policy updates. The model remains close to its reference distribution.
+ - Can slow down adaptation: The model may struggle to explore better responses.
+- **Lower β (Weaker KL Penalty)**
+ - More freedom to update policy: The model can deviate more from the reference.
+ - Faster adaptation but risk of instability: The model might learn reward-hacking behaviors.
+ - Over-optimization risk: If the reward model is flawed, the policy might generate nonsensical outputs.
+- **Original** [DeepSeekMath](https://arxiv.org/abs/2402.03300) paper set this \\( \beta= 0.04 \\)
+
+Now that we understand the components of GRPO, let's see how they work together in a complete example.
+
+## Worked Example with GRPO
+
+To solidify our understanding of GRPO, let's walk through a complete example from start to finish.
+
+### Example Problem
+
+$$\text{Q: Calculate}\space2 + 2 \times 6$$
+
+### Step 1: Group Sampling
+
+First, we generate multiple responses from our model.
+
+Generate \\( (G = 8) \\) responses, \\( 4 \\) of which are correct answer (\\( 14, \text{reward=} 1 \\)) and \\( 4 \\) incorrect \\( \text{(reward= 0)} \\), Therefore:
+
+$${o_1:14(correct), o_2:10 (wrong), o_3:16 (wrong), ... o_G:14(correct)}$$
+
+### Step 2: Advantage Calculation
+
+Next, we calculate the advantage values to determine which responses are better than average:
+
+| Statistic | Value |
+|-----------|-------|
+| Group Average | \\( mean(r_i) = 0.5 \\) |
+| Standard Deviation | \\( std(r_i) = 0.53 \\) |
+| Advantage Value (Correct response) | \\( A_i = \frac{1 - 0.5}{0.53}= 0.94 \\) |
+| Advantage Value (Wrong response) | \\( A_i = \frac{0 - 0.5}{0.53}= -0.94 \\) |
+
+### Step 3: Policy Update
+
+Finally, we update our model to reinforce the correct responses:
+
+- Assuming the probability of old policy (\\( \pi_{\theta_{old}} \\)) for a correct output \\( o_1 \\) is \\( 0.5 \\) and the new policy increases it to \\( 0.7 \\) then:
+$$\text{Ratio}: \frac{0.7}{0.5} = 1.4 →\text{after Clip}\space1.2 \space (\epsilon = 0.2)$$
+- Then when the target function is re-weighted, the model tends to reinforce the generation of correct output, and the \\( \text{KL Divergence} \\) limits the deviation from the reference policy.
+
+With the theoretical understanding in place, let's see how GRPO can be implemented in code.
+
+## Implementation Example
+
+Let's put everything together in a practical example. The following code demonstrates how to implement GRPO in PyTorch.
+
+### 1. Loading the Model and Generating Responses
+
+First, we need to load a model and generate multiple responses for a given question:
+
+```python
+import torch
+import torch.nn.functional as F
+from transformers import AutoModelForCausalLM, AutoTokenizer
+
+# Load the model and tokenizer
+model_name = "Qwen/Qwen2-Math-1.5B"
+model = AutoModelForCausalLM.from_pretrained(model_name)
+tokenizer = AutoTokenizer.from_pretrained(model_name)
+model.eval()
+
+# Move model to GPU if available
+device = torch.device("cuda" if torch.cuda.is_available() else "cpu")
+model.to(device)
+
+# Input prompt
+prompt = "Solve y = 2x + 1 for x = 2, y = " # Correct answer: 5
+inputs = tokenizer(prompt, return_tensors="pt", padding=True)
+input_ids = inputs["input_ids"].to(device) # Shape: (1, prompt_len)
+attention_mask = inputs["attention_mask"].to(device)
+
+# Step 1: Generate 8 responses (B = 2 groups, G = 4 responses per group)
+batch_size, num_generations = 2, 4
+outputs = model.generate(
+ input_ids=input_ids, # Shape: (1, prompt_len)
+ attention_mask=attention_mask,
+ max_new_tokens=1, # seq_len = 1 (single token per response)
+ num_return_sequences=batch_size * num_generations, # 8 responses total
+ do_sample=True,
+ top_k=10,
+ temperature=0.7,
+ pad_token_id=tokenizer.eos_token_id,
+ return_dict_in_generate=True,
+ output_scores=True,
+)
+```
+
+this initial Generation (Before Any Steps) will output sth like this:
+
+```text
+Output 1: 5.0
+Output 2: 6.0
+Output 3: 7.0
+Output 4: 5.0
+Output 5: 10.0
+Output 6: 2.0
+Output 7: 5.0
+Output 8: 5.0
+```
+
+### 2. Calculating Rewards
+
+Now, we need to determine which responses are correct and assign rewards accordingly:
+
+With GRPO, with the same sample prompt, we generate multiple completions. So for instance, for our prompts of `"Solve y = 2x + 1 for x = 2, y = "` and `Solve y = 2x + 1 for x = 4, y = "` we have two group of generated outputs for the given prompt one is say
+- `[5, 6, 7, 5]` and the other is
+- `[10, 2, 9, 9]` while the correct answer is 5 and 9.
+
+Note that in practice these reward scores are achieved by a rule-based reward function that assigns rewards based on the correctness of the response or a more complex neural network-based model that can be trained to assign rewards based on the correctness of the response or a mixed of both. But for sake of simplicity let's say our reward per response is 1 if the response is correct and 0 if it is wrong, therefore;
+```python
+reward_1 = [1, 0, 0, 1]
+reward_2 = [0, 0, 1, 1]
+```
+next we get the group_wise mean and std of the rewards;
+
+```python
+# Shape: (B * G,) = (8,) bc we have 2 groups of 4 generations that we flatten
+rewards = torch.tensor([1, 0, 0, 1, 0, 0, 1, 1], dtype=torch.float32)
+num_generations = 4
+
+# Group rewards: Shape (B, G) = 2, 4)
+rewards_grouped = rewards.view(-1, num_generations)
+
+# Mean per group: Shape (B,) = (2,)
+mean_grouped_rewards = rewards_grouped.mean(dim=1)
+
+# Std per group: Shape (B,) = (2,)
+std_grouped_rewards = rewards_grouped.std(dim=1)
+
+# Broadcast to match rewards and normalize: Shape (B * G,) = (8,)
+# why we need to broadcast? because we need to calculate the advantage values for each response within the group
+mean_grouped_rewards = mean_grouped_rewards.repeat_interleave(num_generations, dim=0)
+std_grouped_rewards = std_grouped_rewards.repeat_interleave(num_generations, dim=0)
+```
+this will output:
+```text
+Grouped Rewards: tensor([[1., 0., 0., 1.],
+ [0., 0., 1., 1.]])
+Mean per group: tensor([0.5000, 0.5000])
+Std per group: tensor([0.5774, 0.5774])
+Broadcasted Mean: tensor([0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000, 0.5000])
+Broadcasted Std: tensor([0.5774, 0.5774, 0.5774, 0.5774, 0.5774, 0.5774, 0.5774, 0.5774])
+```
+Now we can calculate the advantage values for each response:
+```python
+# Advantages: Shape (B * G,) = (8,)
+advantages = (rewards - mean_grouped_rewards) / (std_grouped_rewards + 1e-8)
+```
+this will output:
+```text
+Advantages: tensor([ 0.8659, -0.8660, -0.8660, 0.8659, -0.8660, -0.8660, 0.8659, 0.8659])
+```
+which is coming from the Advantage formula above, so:
+```text
+For reward_1 = [1, 0, 0, 1]:
+1 - 0.5 / 0.5774 ≈ 0.8659
+0 - 0.5 / 0.5774 ≈ -0.8660
+For reward_2 = [0, 0, 1, 1]: Same pattern.
+```
+however, the shape here is `(B*G,) = (8,)` but in practice, we need to have the shape of `(B, G) = (2, 4)` to match the logits shape, right? Therefore, we need to unsqueeze the advantages tensor to have the shape of `(B*G, 1) = (8, 1)` to match the logits shape.
+```python
+# Shape (B * G, 1) = (8, 1) to match the logits shape
+advantages = advantages.unsqueeze(1)
+```
+which will output:
+```text
+Advantages: tensor([[ 0.8659],
+ [-0.8660],
+ [-0.8660],
+ [ 0.8659],
+ [-0.8660],
+ [-0.8660],
+ [ 0.8659],
+ [ 0.8659]])
+```
+now we are good, let's move to the next step of updating the policy model based on the advantage values.
+
+### 3. Updating the Policy
+Finally, we use the advantage values to update our model:
+
+```python
+# Compute probability ratio between new and old policies
+ratio = torch.exp(
+ new_per_token_logps - per_token_logps
+) # Shape: (B*G, seq_len) seq_len is the length of the output i.e. the num of generated tokens so here for simplicity let's assume it is 1 # (8, 1)
+```
+
+Note that the `per_token_logps` can be achieved by passing the generated outputs to the model and get the logits and then apply the softmax function to get the probabilities `F.softmax(logits, dim=-1)`.
+
+```python
+# Clipping Function
+eps = self.cliprange # e.g. 0.2
+pg_losses1 = -advantages * ratio # Shape: (B*G, seq_len) #(8, 1)
+pg_losses2 = -advantages * torch.clamp(
+ ratio, 1.0 - eps, 1.0 + eps
+) # Shape: (B*G, seq_len) #(8, 1)
+pg_loss_max = torch.max(pg_losses1, pg_losses2) # Shape: (B*G, seq_len) #(8, 1)
+
+
+# Now Combine with KL penalty # Shape: (B*G, seq_len) #(8, 1)
+per_token_loss = pg_loss_max + self.beta * per_token_kl
+```
+
+`per_token_kl` can also be calculated as follows:
+
+```python
+# Shape: (B*G, seq_len) #(8, 1)
+per_token_kl = F.kl_div(
+ F.log_softmax(new_per_token_logps, dim=-1),
+ F.softmax(per_token_logps, dim=-1),
+ reduction="none",
+).sum(dim=-1, keepdim=True)
+```
+
+Complete example can be found [here](./basic_example.py). GRPO is also implemented by the excellent TRL team, you can check the implementation [TRL/GRPO_trainer](https://github.com/huggingface/trl/blob/main/trl/trainer/grpo_trainer.py) for more details.
+
+## Summary and Next Steps
+
+Congratulations! You've now learned about Group Relative Policy Optimization (GRPO). To recap what we've covered:
+
+1. GRPO compares multiple outputs within a group to determine which ones are better than others, without requiring a separate value model.
+2. The advantage calculation standardizes rewards to identify which responses are above or below average.
+3. The policy update uses a clipped objective function with a KL divergence penalty to ensure stable learning.
+
+This approach is particularly powerful for mathematical reasoning tasks, where correctness can be objectively verified. The GRPO method allows for more efficient training compared to traditional RLHF approaches that require a separate critic model.
+
+As you continue exploring GRPO, consider experimenting with different group sizes, reward functions, and KL penalty coefficients to see how they affect your model's performance.
+
+Happy training! 🚀
+
+## References
+1. [RLHF Book by Nathan Lambert](https://github.com/natolambert/rlhf-book)
+2. [DeepSeek-V3 Technical Report](https://huggingface.co/papers/2412.19437)
+3. [DeepSeekMath](https://huggingface.co/papers/2402.03300)
diff --git a/chapters/en/chapter12/4.mdx b/chapters/en/chapter12/4.mdx
index 769bb561c..44eb52085 100644
--- a/chapters/en/chapter12/4.mdx
+++ b/chapters/en/chapter12/4.mdx
@@ -1,234 +1,234 @@
-# Implementing GRPO in TRL
-
-In this page, we'll learn how to implement Group Relative Policy Optimization (GRPO) using the Transformer Reinforcement Learning (TRL) library. We'll focus on practical implementation with minimal code.
-
-We'll explore the core concepts of GRPO as they are embodied in TRL's GRPOTrainer, using snippets from the official TRL documentation to guide us.
-
-> [!TIP]
-> This chapter is aimed at TRL beginners. If you are already familiar with TRL, you might want to also check out the [Open R1 implementation](https://github.com/huggingface/open-r1/blob/main/src/open_r1/grpo.py) of GRPO.
-
-First, let's remind ourselves of some of the important concepts of GRPO algorithm:
-
-- Group Formation: The model generates multiple completions for each prompt.
-- Preference Learning: The model learns from a reward function that compares groups of completions.
-- Training Configuration: The model uses a configuration to control the training process.
-
-What do we need to do to implement GRPO?
-
-- Define a dataset of prompts.
-- Define a reward function that takes a list of completions and returns a list of rewards.
-- Configure the training process with a GRPOConfig.
-- Train the model using the GRPOTrainer.
-
-Here's a minimal example to get started with GRPO training:
-
-```python
-from trl import GRPOTrainer, GRPOConfig
-from datasets import load_dataset
-
-# 1. Load your dataset
-dataset = load_dataset("your_dataset", split="train")
-
-
-# 2. Define a simple reward function
-def reward_func(completions, **kwargs):
- """Example: Reward longer completions"""
- return [float(len(completion)) for completion in completions]
-
-
-# 3. Configure training
-training_args = GRPOConfig(
- output_dir="output",
- num_train_epochs=3,
- per_device_train_batch_size=4,
- gradient_accumulation_steps=2,
- logging_steps=10,
-)
-
-# 4. Initialize and train
-trainer = GRPOTrainer(
- model="your_model", # e.g. "Qwen/Qwen2-0.5B-Instruct"
- args=training_args,
- train_dataset=dataset,
- reward_funcs=reward_func,
-)
-trainer.train()
-```
-
-## Key Components
-
-### 1. Dataset Format
-
-Your dataset should contain prompts that the model will respond to. The GRPO trainer will generate multiple completions for each prompt and use the reward function to compare them.
-
-### 2. Reward Function
-
-The reward function is crucial - it determines how the model learns. Here are two practical examples:
-
-```python
-# Example 1: Reward based on completion length
-def reward_length(completions, **kwargs):
- return [float(len(completion)) for completion in completions]
-
-
-# Example 2: Reward based on matching a pattern
-import re
-
-
-def reward_format(completions, **kwargs):
- pattern = r"^.*?.*?$"
- return [1.0 if re.match(pattern, c) else 0.0 for c in completions]
-```
-
-### 3. Training Configuration
-
-Key parameters to consider in `GRPOConfig`:
-
-```python
-training_args = GRPOConfig(
- # Essential parameters
- output_dir="output",
- num_train_epochs=3,
- num_generation=4, # Number of completions to generate for each prompt
- per_device_train_batch_size=4, # We want to get all generations in one device batch
- # Optional but useful
- gradient_accumulation_steps=2,
- learning_rate=1e-5,
- logging_steps=10,
- # GRPO specific (optional)
- use_vllm=True, # Speed up generation
-)
-```
-
-The `num_generation` parameter is particularly important for GRPO as it defines the group size - how many different completions the model will generate for each prompt. This is a key differentiator from other RL methods:
-
-- Too small (e.g., 2-3): May not provide enough diversity for meaningful comparisons
-- Recommended (4-16): Provides good balance between diversity and computational efficiency
-- Larger values: May improve learning but significantly increases computational cost
-
-The group size should be chosen based on your computational resources and the complexity of your task. For simple tasks, smaller groups (4-8) may be sufficient, while more complex reasoning tasks might benefit from larger groups (8-16).
-
-## Tips for Success
-
-1. **Memory Management**: Adjust `per_device_train_batch_size` and `gradient_accumulation_steps` based on your GPU memory.
-2. **Speed**: Enable `use_vllm=True` for faster generation if your model is supported.
-3. **Monitoring**: Watch the logged metrics during training:
- - `reward`: Average reward across completions
- - `reward_std`: Standard deviation within reward groups
- - `kl`: KL divergence from reference model
-
-## Reward Function Design
-
-The DeepSeek R1 paper demonstrates several effective approaches to reward function design that you can adapt for your own GRPO implementation:
-
-### 1. Length-Based Rewards
-
-One of the easiest rewards to implement is a length-based reward. You can reward longer completions:
-
-```python
-def reward_len(completions, **kwargs):
- ideal_length = 20
- return [-abs(ideal_length - len(completion)) for completion in completions]
-```
-
-This reward function penalizes completions that are too short or too long, encouraging the model to generate completions that are close to the ideal length of 20 tokens.
-
-
-
-
-
-## 2. Rule-Based Rewards for Verifiable Tasks
-
-For tasks with objectively correct answers (like mathematics or coding), you can implement rule-based reward functions:
-
-```python
-def problem_reward(completions, answers, **kwargs):
- """Reward function for math problems with verifiable answers
- completions: list of completions to evaluate
- answers: list of answers to the problems from the dataset
- """
-
- rewards = []
- for completion, correct_answer in zip(completions, answers):
- # Extract the answer from the completion
- try:
- # This is a simplified example - you'd need proper parsing
- answer = extract_final_answer(completion)
- # Binary reward: 1 for correct, 0 for incorrect
- reward = 1.0 if answer == correct_answer else 0.0
- rewards.append(reward)
- except:
- # If we can't parse an answer, give a low reward
- rewards.append(0.0)
-
- return rewards
-```
-
-
-
-
-
-## 3. Format-Based Rewards
-
-You can also reward proper formatting, which was important in the DeepSeek R1 training:
-
-```python
-def format_reward(completions, **kwargs):
- """Reward completions that follow the desired format"""
- # Example: Check if the completion follows a think-then-answer format
- pattern = r"(.*?)\s*(.*?)"
-
- rewards = []
- for completion in completions:
- match = re.search(pattern, completion, re.DOTALL)
- if match:
- # Check if there's substantial content in both sections
- think_content = match.group(1).strip()
- answer_content = match.group(2).strip()
-
- if len(think_content) > 20 and len(answer_content) > 0:
- rewards.append(1.0)
- else:
- rewards.append(
- 0.5
- ) # Partial reward for correct format but limited content
- else:
- rewards.append(0.0) # No reward for incorrect format
-
- return rewards
-```
-
-
-
-
-
-
-
-These examples demonstrate how you can implement reward functions inspired by the DeepSeek R1 training process, focusing on correctness, formatting, and combined signals.
-
-
-## That's it!
-
-In the next section, you will follow an exercise to implement GRPO in TRL.
-
+# Implementing GRPO in TRL
+
+In this page, we'll learn how to implement Group Relative Policy Optimization (GRPO) using the Transformer Reinforcement Learning (TRL) library. We'll focus on practical implementation with minimal code.
+
+We'll explore the core concepts of GRPO as they are embodied in TRL's GRPOTrainer, using snippets from the official TRL documentation to guide us.
+
+> [!TIP]
+> This chapter is aimed at TRL beginners. If you are already familiar with TRL, you might want to also check out the [Open R1 implementation](https://github.com/huggingface/open-r1/blob/main/src/open_r1/grpo.py) of GRPO.
+
+First, let's remind ourselves of some of the important concepts of GRPO algorithm:
+
+- Group Formation: The model generates multiple completions for each prompt.
+- Preference Learning: The model learns from a reward function that compares groups of completions.
+- Training Configuration: The model uses a configuration to control the training process.
+
+What do we need to do to implement GRPO?
+
+- Define a dataset of prompts.
+- Define a reward function that takes a list of completions and returns a list of rewards.
+- Configure the training process with a GRPOConfig.
+- Train the model using the GRPOTrainer.
+
+Here's a minimal example to get started with GRPO training:
+
+```python
+from trl import GRPOTrainer, GRPOConfig
+from datasets import load_dataset
+
+# 1. Load your dataset
+dataset = load_dataset("your_dataset", split="train")
+
+
+# 2. Define a simple reward function
+def reward_func(completions, **kwargs):
+ """Example: Reward longer completions"""
+ return [float(len(completion)) for completion in completions]
+
+
+# 3. Configure training
+training_args = GRPOConfig(
+ output_dir="output",
+ num_train_epochs=3,
+ per_device_train_batch_size=4,
+ gradient_accumulation_steps=2,
+ logging_steps=10,
+)
+
+# 4. Initialize and train
+trainer = GRPOTrainer(
+ model="your_model", # e.g. "Qwen/Qwen2-0.5B-Instruct"
+ args=training_args,
+ train_dataset=dataset,
+ reward_funcs=reward_func,
+)
+trainer.train()
+```
+
+## Key Components
+
+### 1. Dataset Format
+
+Your dataset should contain prompts that the model will respond to. The GRPO trainer will generate multiple completions for each prompt and use the reward function to compare them.
+
+### 2. Reward Function
+
+The reward function is crucial - it determines how the model learns. Here are two practical examples:
+
+```python
+# Example 1: Reward based on completion length
+def reward_length(completions, **kwargs):
+ return [float(len(completion)) for completion in completions]
+
+
+# Example 2: Reward based on matching a pattern
+import re
+
+
+def reward_format(completions, **kwargs):
+ pattern = r"^.*?.*?$"
+ return [1.0 if re.match(pattern, c) else 0.0 for c in completions]
+```
+
+### 3. Training Configuration
+
+Key parameters to consider in `GRPOConfig`:
+
+```python
+training_args = GRPOConfig(
+ # Essential parameters
+ output_dir="output",
+ num_train_epochs=3,
+ num_generation=4, # Number of completions to generate for each prompt
+ per_device_train_batch_size=4, # We want to get all generations in one device batch
+ # Optional but useful
+ gradient_accumulation_steps=2,
+ learning_rate=1e-5,
+ logging_steps=10,
+ # GRPO specific (optional)
+ use_vllm=True, # Speed up generation
+)
+```
+
+The `num_generation` parameter is particularly important for GRPO as it defines the group size - how many different completions the model will generate for each prompt. This is a key differentiator from other RL methods:
+
+- Too small (e.g., 2-3): May not provide enough diversity for meaningful comparisons
+- Recommended (4-16): Provides good balance between diversity and computational efficiency
+- Larger values: May improve learning but significantly increases computational cost
+
+The group size should be chosen based on your computational resources and the complexity of your task. For simple tasks, smaller groups (4-8) may be sufficient, while more complex reasoning tasks might benefit from larger groups (8-16).
+
+## Tips for Success
+
+1. **Memory Management**: Adjust `per_device_train_batch_size` and `gradient_accumulation_steps` based on your GPU memory.
+2. **Speed**: Enable `use_vllm=True` for faster generation if your model is supported.
+3. **Monitoring**: Watch the logged metrics during training:
+ - `reward`: Average reward across completions
+ - `reward_std`: Standard deviation within reward groups
+ - `kl`: KL divergence from reference model
+
+## Reward Function Design
+
+The DeepSeek R1 paper demonstrates several effective approaches to reward function design that you can adapt for your own GRPO implementation:
+
+### 1. Length-Based Rewards
+
+One of the easiest rewards to implement is a length-based reward. You can reward longer completions:
+
+```python
+def reward_len(completions, **kwargs):
+ ideal_length = 20
+ return [-abs(ideal_length - len(completion)) for completion in completions]
+```
+
+This reward function penalizes completions that are too short or too long, encouraging the model to generate completions that are close to the ideal length of 20 tokens.
+
+
+
+
+
+## 2. Rule-Based Rewards for Verifiable Tasks
+
+For tasks with objectively correct answers (like mathematics or coding), you can implement rule-based reward functions:
+
+```python
+def problem_reward(completions, answers, **kwargs):
+ """Reward function for math problems with verifiable answers
+ completions: list of completions to evaluate
+ answers: list of answers to the problems from the dataset
+ """
+
+ rewards = []
+ for completion, correct_answer in zip(completions, answers):
+ # Extract the answer from the completion
+ try:
+ # This is a simplified example - you'd need proper parsing
+ answer = extract_final_answer(completion)
+ # Binary reward: 1 for correct, 0 for incorrect
+ reward = 1.0 if answer == correct_answer else 0.0
+ rewards.append(reward)
+ except:
+ # If we can't parse an answer, give a low reward
+ rewards.append(0.0)
+
+ return rewards
+```
+
+
+
+
+
+## 3. Format-Based Rewards
+
+You can also reward proper formatting, which was important in the DeepSeek R1 training:
+
+```python
+def format_reward(completions, **kwargs):
+ """Reward completions that follow the desired format"""
+ # Example: Check if the completion follows a think-then-answer format
+ pattern = r"(.*?)\s*(.*?)"
+
+ rewards = []
+ for completion in completions:
+ match = re.search(pattern, completion, re.DOTALL)
+ if match:
+ # Check if there's substantial content in both sections
+ think_content = match.group(1).strip()
+ answer_content = match.group(2).strip()
+
+ if len(think_content) > 20 and len(answer_content) > 0:
+ rewards.append(1.0)
+ else:
+ rewards.append(
+ 0.5
+ ) # Partial reward for correct format but limited content
+ else:
+ rewards.append(0.0) # No reward for incorrect format
+
+ return rewards
+```
+
+
+
+
+
+
+
+These examples demonstrate how you can implement reward functions inspired by the DeepSeek R1 training process, focusing on correctness, formatting, and combined signals.
+
+
+## That's it!
+
+In the next section, you will follow an exercise to implement GRPO in TRL.
+
diff --git a/chapters/en/chapter12/5.mdx b/chapters/en/chapter12/5.mdx
index a00b205cd..6c82914b3 100644
--- a/chapters/en/chapter12/5.mdx
+++ b/chapters/en/chapter12/5.mdx
@@ -1,234 +1,234 @@
-
-
-# Practical Exercise: Fine-tune a model with GRPO
-
-Now that you've seen the theory, let's put it into practice! In this exercise, you'll fine-tune a model with GRPO.
-
-> [!TIP]
-> This exercise was written by LLM fine-tuning expert [@mlabonne](https://huggingface.co/mlabonne).
-
-## Install dependencies
-
-First, let's install the dependencies for this exercise.
-
-```bash
-!pip install -qqq datasets==3.2.0 transformers==4.47.1 trl==0.14.0 peft==0.14.0 accelerate==1.2.1 bitsandbytes==0.45.2 wandb==0.19.7 --progress-bar off
-!pip install -qqq flash-attn --no-build-isolation --progress-bar off
-```
-
-Now we'll import the necessary libraries.
-
-```python
-import torch
-from datasets import load_dataset
-from peft import LoraConfig, get_peft_model
-from transformers import AutoModelForCausalLM, AutoTokenizer
-from trl import GRPOConfig, GRPOTrainer
-```
-
-## Import and log in to Weights & Biases
-
-Weights & Biases is a tool for logging and monitoring your experiments. We'll use it to log our fine-tuning process.
-
-```python
-import wandb
-
-wandb.login()
-```
-
-You can do this exercise without logging in to Weights & Biases, but it's recommended to do so to track your experiments and interpret the results.
-
-## Load the dataset
-
-Now, let's load the dataset. In this case, we'll use the [`mlabonne/smoltldr`](https://huggingface.co/datasets/mlabonne/smoltldr) dataset, which contains a list of short stories.
-
-```python
-dataset = load_dataset("mlabonne/smoltldr")
-print(dataset)
-```
-
-## Load model
-
-Now, let's load the model.
-
-For this exercise, we'll use the [`SmolLM2-135M`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M) model.
-
-This is a small 135M parameter model that runs on limited hardware. This makes the model ideal for learning, but it's not the most powerful model out there. If you have access to more powerful hardware, you can try to fine-tune a larger model like [`SmolLM2-1.7B`](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B).
-
-```python
-model_id = "HuggingFaceTB/SmolLM-135M-Instruct"
-model = AutoModelForCausalLM.from_pretrained(
- model_id,
- torch_dtype="auto",
- device_map="auto",
- attn_implementation="flash_attention_2",
-)
-tokenizer = AutoTokenizer.from_pretrained(model_id)
-```
-
-## Load LoRA
-
-Now, let's load the LoRA configuration. We'll take advantage of LoRA to reduce the number of trainable parameters, and in turn the memory footprint we need to fine-tune the model.
-
-If you're not familiar with LoRA, you can read more about it in [Chapter 11](https://huggingface.co/learn/course/en/chapter11/3).
-
-```python
-# Load LoRA
-lora_config = LoraConfig(
- task_type="CAUSAL_LM",
- r=16,
- lora_alpha=32,
- target_modules="all-linear",
-)
-model = get_peft_model(model, lora_config)
-print(model.print_trainable_parameters())
-```
-
-```sh
-Total trainable parameters: 135M
-```
-
-## Define the reward function
-
-As mentioned in the previous section, GRPO can use any reward function to improve the model. In this case, we'll use a simple reward function that encourages the model to generate text that is 50 tokens long.
-
-```python
-# Reward function
-ideal_length = 50
-
-
-def reward_len(completions, **kwargs):
- return [-abs(ideal_length - len(completion)) for completion in completions]
-```
-
-## Define the training arguments
-
-Now, let's define the training arguments. We'll use the `GRPOConfig` class to define the training arguments in a typical `transformers` style.
-
-If this is the first time you're defining training arguments, you can check the [TrainingArguments](https://huggingface.co/docs/transformers/en/main_classes/trainer#trainingarguments) class for more information, or [Chapter 2](https://huggingface.co/learn/course/en/chapter2/1) for a detailed introduction.
-
-```python
-# Training arguments
-training_args = GRPOConfig(
- output_dir="GRPO",
- learning_rate=2e-5,
- per_device_train_batch_size=8,
- gradient_accumulation_steps=2,
- max_prompt_length=512,
- max_completion_length=96,
- num_generations=8,
- optim="adamw_8bit",
- num_train_epochs=1,
- bf16=True,
- report_to=["wandb"],
- remove_unused_columns=False,
- logging_steps=1,
-)
-```
-
-Now, we can initialize the trainer with model, dataset, and training arguments and start training.
-
-```python
-# Trainer
-trainer = GRPOTrainer(
- model=model,
- reward_funcs=[reward_len],
- args=training_args,
- train_dataset=dataset["train"],
-)
-
-# Train model
-wandb.init(project="GRPO")
-trainer.train()
-```
-
-Training takes around 1 hour on a single A10G GPU which is available on Google Colab or via Hugging Face Spaces.
-
-## Push the model to the Hub during training
-
-If we set the `push_to_hub` argument to `True` and the `model_id` argument to a valid model name, the model will be pushed to the Hugging Face Hub whilst we're training. This is useful if you want to start vibe testing the model straight away!
-
-## Interpret training results
-
-`GRPOTrainer` logs the reward from your reward function, the loss, and a range of other metrics.
-
-We will focus on the reward from the reward function and the loss.
-
-As you can see, the reward from the reward function moves closer to 0 as the model learns. This is a good sign that the model is learning to generate text of the correct length.
-
-
-
-You might notice that the loss starts at zero and then increases during training, which may seem counterintuitive. This behavior is expected in GRPO and is directly related to the mathematical formulation of the algorithm. The loss in GRPO is proportional to the KL divergence (the cap relative to original policy) . As training progresses, the model learns to generate text that better matches the reward function, causing it to diverge more from its initial policy. This increasing divergence is reflected in the rising loss value, which actually indicates that the model is successfully adapting to optimize for the reward function.
-
-
-
-## Save and publish the model
-
-Let's share the model with the community!
-
-```python
-merged_model = trainer.model.merge_and_unload()
-merged_model.push_to_hub(
- "SmolGRPO-135M", private=False, tags=["GRPO", "Reasoning-Course"]
-)
-```
-
-## Generate text
-
-🎉 You've successfully fine-tuned a model with GRPO! Now, let's generate some text with the model.
-
-First, we'll define a really long document!
-
-```python
-prompt = """
-# A long document about the Cat
-
-The cat (Felis catus), also referred to as the domestic cat or house cat, is a small
-domesticated carnivorous mammal. It is the only domesticated species of the family Felidae.
-Advances in archaeology and genetics have shown that the domestication of the cat occurred
-in the Near East around 7500 BC. It is commonly kept as a pet and farm cat, but also ranges
-freely as a feral cat avoiding human contact. It is valued by humans for companionship and
-its ability to kill vermin. Its retractable claws are adapted to killing small prey species
-such as mice and rats. It has a strong, flexible body, quick reflexes, and sharp teeth,
-and its night vision and sense of smell are well developed. It is a social species,
-but a solitary hunter and a crepuscular predator. Cat communication includes
-vocalizations—including meowing, purring, trilling, hissing, growling, and grunting—as
-well as body language. It can hear sounds too faint or too high in frequency for human ears,
-such as those made by small mammals. It secretes and perceives pheromones.
-"""
-
-messages = [
- {"role": "user", "content": prompt},
-]
-```
-
-Now, we can generate text with the model.
-
-```python
-# Generate text
-from transformers import pipeline
-
-generator = pipeline("text-generation", model="SmolGRPO-135M")
-
-## Or use the model and tokenizer we defined earlier
-# generator = pipeline("text-generation", model=model, tokenizer=tokenizer)
-
-generate_kwargs = {
- "max_new_tokens": 256,
- "do_sample": True,
- "temperature": 0.5,
- "min_p": 0.1,
-}
-
-generated_text = generator(messages, generate_kwargs=generate_kwargs)
-
-print(generated_text)
-```
-
-# Conclusion
-
-In this chapter, we've seen how to fine-tune a model with GRPO. We've also seen how to interpret the training results and generate text with the model.
+
+
+# Practical Exercise: Fine-tune a model with GRPO
+
+Now that you've seen the theory, let's put it into practice! In this exercise, you'll fine-tune a model with GRPO.
+
+> [!TIP]
+> This exercise was written by LLM fine-tuning expert [@mlabonne](https://huggingface.co/mlabonne).
+
+## Install dependencies
+
+First, let's install the dependencies for this exercise.
+
+```bash
+!pip install -qqq datasets==3.2.0 transformers==4.47.1 trl==0.14.0 peft==0.14.0 accelerate==1.2.1 bitsandbytes==0.45.2 wandb==0.19.7 --progress-bar off
+!pip install -qqq flash-attn --no-build-isolation --progress-bar off
+```
+
+Now we'll import the necessary libraries.
+
+```python
+import torch
+from datasets import load_dataset
+from peft import LoraConfig, get_peft_model
+from transformers import AutoModelForCausalLM, AutoTokenizer
+from trl import GRPOConfig, GRPOTrainer
+```
+
+## Import and log in to Weights & Biases
+
+Weights & Biases is a tool for logging and monitoring your experiments. We'll use it to log our fine-tuning process.
+
+```python
+import wandb
+
+wandb.login()
+```
+
+You can do this exercise without logging in to Weights & Biases, but it's recommended to do so to track your experiments and interpret the results.
+
+## Load the dataset
+
+Now, let's load the dataset. In this case, we'll use the [`mlabonne/smoltldr`](https://huggingface.co/datasets/mlabonne/smoltldr) dataset, which contains a list of short stories.
+
+```python
+dataset = load_dataset("mlabonne/smoltldr")
+print(dataset)
+```
+
+## Load model
+
+Now, let's load the model.
+
+For this exercise, we'll use the [`SmolLM2-135M`](https://huggingface.co/HuggingFaceTB/SmolLM2-135M) model.
+
+This is a small 135M parameter model that runs on limited hardware. This makes the model ideal for learning, but it's not the most powerful model out there. If you have access to more powerful hardware, you can try to fine-tune a larger model like [`SmolLM2-1.7B`](https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B).
+
+```python
+model_id = "HuggingFaceTB/SmolLM-135M-Instruct"
+model = AutoModelForCausalLM.from_pretrained(
+ model_id,
+ torch_dtype="auto",
+ device_map="auto",
+ attn_implementation="flash_attention_2",
+)
+tokenizer = AutoTokenizer.from_pretrained(model_id)
+```
+
+## Load LoRA
+
+Now, let's load the LoRA configuration. We'll take advantage of LoRA to reduce the number of trainable parameters, and in turn the memory footprint we need to fine-tune the model.
+
+If you're not familiar with LoRA, you can read more about it in [Chapter 11](https://huggingface.co/learn/course/en/chapter11/3).
+
+```python
+# Load LoRA
+lora_config = LoraConfig(
+ task_type="CAUSAL_LM",
+ r=16,
+ lora_alpha=32,
+ target_modules="all-linear",
+)
+model = get_peft_model(model, lora_config)
+print(model.print_trainable_parameters())
+```
+
+```sh
+Total trainable parameters: 135M
+```
+
+## Define the reward function
+
+As mentioned in the previous section, GRPO can use any reward function to improve the model. In this case, we'll use a simple reward function that encourages the model to generate text that is 50 tokens long.
+
+```python
+# Reward function
+ideal_length = 50
+
+
+def reward_len(completions, **kwargs):
+ return [-abs(ideal_length - len(completion)) for completion in completions]
+```
+
+## Define the training arguments
+
+Now, let's define the training arguments. We'll use the `GRPOConfig` class to define the training arguments in a typical `transformers` style.
+
+If this is the first time you're defining training arguments, you can check the [TrainingArguments](https://huggingface.co/docs/transformers/en/main_classes/trainer#trainingarguments) class for more information, or [Chapter 2](https://huggingface.co/learn/course/en/chapter2/1) for a detailed introduction.
+
+```python
+# Training arguments
+training_args = GRPOConfig(
+ output_dir="GRPO",
+ learning_rate=2e-5,
+ per_device_train_batch_size=8,
+ gradient_accumulation_steps=2,
+ max_prompt_length=512,
+ max_completion_length=96,
+ num_generations=8,
+ optim="adamw_8bit",
+ num_train_epochs=1,
+ bf16=True,
+ report_to=["wandb"],
+ remove_unused_columns=False,
+ logging_steps=1,
+)
+```
+
+Now, we can initialize the trainer with model, dataset, and training arguments and start training.
+
+```python
+# Trainer
+trainer = GRPOTrainer(
+ model=model,
+ reward_funcs=[reward_len],
+ args=training_args,
+ train_dataset=dataset["train"],
+)
+
+# Train model
+wandb.init(project="GRPO")
+trainer.train()
+```
+
+Training takes around 1 hour on a single A10G GPU which is available on Google Colab or via Hugging Face Spaces.
+
+## Push the model to the Hub during training
+
+If we set the `push_to_hub` argument to `True` and the `model_id` argument to a valid model name, the model will be pushed to the Hugging Face Hub whilst we're training. This is useful if you want to start vibe testing the model straight away!
+
+## Interpret training results
+
+`GRPOTrainer` logs the reward from your reward function, the loss, and a range of other metrics.
+
+We will focus on the reward from the reward function and the loss.
+
+As you can see, the reward from the reward function moves closer to 0 as the model learns. This is a good sign that the model is learning to generate text of the correct length.
+
+
+
+You might notice that the loss starts at zero and then increases during training, which may seem counterintuitive. This behavior is expected in GRPO and is directly related to the mathematical formulation of the algorithm. The loss in GRPO is proportional to the KL divergence (the cap relative to original policy) . As training progresses, the model learns to generate text that better matches the reward function, causing it to diverge more from its initial policy. This increasing divergence is reflected in the rising loss value, which actually indicates that the model is successfully adapting to optimize for the reward function.
+
+
+
+## Save and publish the model
+
+Let's share the model with the community!
+
+```python
+merged_model = trainer.model.merge_and_unload()
+merged_model.push_to_hub(
+ "SmolGRPO-135M", private=False, tags=["GRPO", "Reasoning-Course"]
+)
+```
+
+## Generate text
+
+🎉 You've successfully fine-tuned a model with GRPO! Now, let's generate some text with the model.
+
+First, we'll define a really long document!
+
+```python
+prompt = """
+# A long document about the Cat
+
+The cat (Felis catus), also referred to as the domestic cat or house cat, is a small
+domesticated carnivorous mammal. It is the only domesticated species of the family Felidae.
+Advances in archaeology and genetics have shown that the domestication of the cat occurred
+in the Near East around 7500 BC. It is commonly kept as a pet and farm cat, but also ranges
+freely as a feral cat avoiding human contact. It is valued by humans for companionship and
+its ability to kill vermin. Its retractable claws are adapted to killing small prey species
+such as mice and rats. It has a strong, flexible body, quick reflexes, and sharp teeth,
+and its night vision and sense of smell are well developed. It is a social species,
+but a solitary hunter and a crepuscular predator. Cat communication includes
+vocalizations—including meowing, purring, trilling, hissing, growling, and grunting—as
+well as body language. It can hear sounds too faint or too high in frequency for human ears,
+such as those made by small mammals. It secretes and perceives pheromones.
+"""
+
+messages = [
+ {"role": "user", "content": prompt},
+]
+```
+
+Now, we can generate text with the model.
+
+```python
+# Generate text
+from transformers import pipeline
+
+generator = pipeline("text-generation", model="SmolGRPO-135M")
+
+## Or use the model and tokenizer we defined earlier
+# generator = pipeline("text-generation", model=model, tokenizer=tokenizer)
+
+generate_kwargs = {
+ "max_new_tokens": 256,
+ "do_sample": True,
+ "temperature": 0.5,
+ "min_p": 0.1,
+}
+
+generated_text = generator(messages, generate_kwargs=generate_kwargs)
+
+print(generated_text)
+```
+
+# Conclusion
+
+In this chapter, we've seen how to fine-tune a model with GRPO. We've also seen how to interpret the training results and generate text with the model.
diff --git a/chapters/en/chapter12/6.mdx b/chapters/en/chapter12/6.mdx
index 7a60ea6e9..84405109a 100644
--- a/chapters/en/chapter12/6.mdx
+++ b/chapters/en/chapter12/6.mdx
@@ -1,367 +1,367 @@
-
-
-# Practical Exercise: GRPO with Unsloth
-
-In this exercise, you'll fine-tune a model with GRPO (Group Relative Policy Optimization) using Unsloth, to improve a model's reasoning capabilities. We covered GRPO in [Chapter 3](/course/chapter3/3).
-
-Unsloth is a library that accelerates LLM fine-tuning, making it possible to train models faster and with less computational resources. Unsloth is plugs into TRL, so we'll build on what we learned in the previous sections, and adapt it for Unsloth specifics.
-
-> [!TIP]
-> This exercise can be run on a free Google Colab T4 GPU. For the best experience, follow along with the notebook linked above and try it out yourself.
-
-## Install dependencies
-
-First, let's install the necessary libraries. We'll need Unsloth for the accelerated fine-tuning and vLLM for fast inference.
-
-```bash
-pip install unsloth vllm
-pip install --upgrade pillow
-```
-
-## Setting up Unsloth
-
-Unsloth provides a class (`FastLanguageModel`) that integrates transformers with Unsloth optimizations. Let's import it:
-
-```python
-from unsloth import FastLanguageModel
-```
-
-Now, let's load Google's Gemma 3 1B Instruct model and configure it for fine-tuning:
-
-```python
-from unsloth import FastLanguageModel
-import torch
-
-max_seq_length = 1024 # Can increase for longer reasoning traces
-lora_rank = 32 # Larger rank = smarter, but slower
-
-model, tokenizer = FastLanguageModel.from_pretrained(
- model_name="google/gemma-3-1b-it",
- max_seq_length=max_seq_length,
- load_in_4bit=True, # False for LoRA 16bit
- fast_inference=True, # Enable vLLM fast inference
- max_lora_rank=lora_rank,
- gpu_memory_utilization=0.6, # Reduce if out of memory
-)
-
-model = FastLanguageModel.get_peft_model(
- model,
- r=lora_rank, # Choose any number > 0 ! Suggested 8, 16, 32, 64, 128
- target_modules=[
- "q_proj",
- "k_proj",
- "v_proj",
- "o_proj",
- "gate_proj",
- "up_proj",
- "down_proj",
- ], # Remove QKVO if out of memory
- lora_alpha=lora_rank,
- use_gradient_checkpointing="unsloth", # Enable long context finetuning
- random_state=3407,
-)
-```
-
-This code loads the model in 4-bit quantization to save memory and applies LoRA (Low-Rank Adaptation) for efficient fine-tuning. The `target_modules` parameter specifies which layers of the model to fine-tune, and `use_gradient_checkpointing` enables training with longer contexts.
-
-> [!TIP]
-> We won't cover the details of LoRA in this chapter, but you can learn more in [Chapter 11](/course/chapter11/3).
-
-## Data Preparation
-
-For this exercise, we'll use the GSM8K dataset, which contains grade school math problems. We'll format the data to encourage the model to show its reasoning before providing an answer.
-
-First, we will define the format of the prompts and answers:
-
-```python
-# Define the system prompt that instructs the model to use a specific format
-SYSTEM_PROMPT = """
-Respond in the following format:
-
-...
-
-
-...
-
-"""
-
-XML_COT_FORMAT = """\
-
-{reasoning}
-
-
-{answer}
-
-"""
-```
-
-Now, let's prepare the dataset:
-
-```python
-import re
-from datasets import load_dataset, Dataset
-
-
-# Helper functions to extract answers from different formats
-def extract_xml_answer(text: str) -> str:
- answer = text.split("")[-1]
- answer = answer.split("")[0]
- return answer.strip()
-
-
-def extract_hash_answer(text: str) -> str | None:
- if "####" not in text:
- return None
- return text.split("####")[1].strip()
-
-
-# Function to prepare the GSM8K dataset
-def get_gsm8k_questions(split="train") -> Dataset:
- data = load_dataset("openai/gsm8k", "main")[split]
- data = data.map(
- lambda x: {
- "prompt": [
- {"role": "system", "content": SYSTEM_PROMPT},
- {"role": "user", "content": x["question"]},
- ],
- "answer": extract_hash_answer(x["answer"]),
- }
- )
- return data
-
-
-dataset = get_gsm8k_questions()
-```
-
-The dataset is prepared by extracting the answer from the dataset and formatting it as a string.
-
-## Defining Reward Functions
-
-As we discussed in [an earlier page](/course/chapter13/4), GRPO can use reward functions to guide the model's learning based on verifiable criteria like length and formatting.
-
-In this exercise, we'll define several reward functions that encourage different aspects of good reasoning. For example, we'll reward the model for providing an integer answer, and for following the strict format.
-
-```python
-# Reward function that checks if the answer is correct
-def correctness_reward_func(prompts, completions, answer, **kwargs) -> list[float]:
- responses = [completion[0]["content"] for completion in completions]
- q = prompts[0][-1]["content"]
- extracted_responses = [extract_xml_answer(r) for r in responses]
- print(
- "-" * 20,
- f"Question:\n{q}",
- f"\nAnswer:\n{answer[0]}",
- f"\nResponse:\n{responses[0]}",
- f"\nExtracted:\n{extracted_responses[0]}",
- )
- return [2.0 if r == a else 0.0 for r, a in zip(extracted_responses, answer)]
-
-
-# Reward function that checks if the answer is an integer
-def int_reward_func(completions, **kwargs) -> list[float]:
- responses = [completion[0]["content"] for completion in completions]
- extracted_responses = [extract_xml_answer(r) for r in responses]
- return [0.5 if r.isdigit() else 0.0 for r in extracted_responses]
-
-
-# Reward function that checks if the completion follows the strict format
-def strict_format_reward_func(completions, **kwargs) -> list[float]:
- pattern = r"^\n.*?\n\n\n.*?\n\n$"
- responses = [completion[0]["content"] for completion in completions]
- matches = [re.match(pattern, r) for r in responses]
- return [0.5 if match else 0.0 for match in matches]
-
-
-# Reward function that checks if the completion follows a more relaxed format
-def soft_format_reward_func(completions, **kwargs) -> list[float]:
- pattern = r".*?\s*.*?"
- responses = [completion[0]["content"] for completion in completions]
- matches = [re.match(pattern, r) for r in responses]
- return [0.5 if match else 0.0 for match in matches]
-
-
-# Reward function that counts XML tags and penalizes extra content
-def count_xml(text) -> float:
- count = 0.0
- if text.count("\n") == 1:
- count += 0.125
- if text.count("\n\n") == 1:
- count += 0.125
- if text.count("\n\n") == 1:
- count += 0.125
- count -= len(text.split("\n\n")[-1]) * 0.001
- if text.count("\n") == 1:
- count += 0.125
- count -= (len(text.split("\n")[-1]) - 1) * 0.001
- return count
-
-
-def xmlcount_reward_func(completions, **kwargs) -> list[float]:
- contents = [completion[0]["content"] for completion in completions]
- return [count_xml(c) for c in contents]
-```
-
-These reward functions serve different purposes:
-
-| Reward Function | Purpose |
-|-----------------|---------|
-| `correctness_reward_func` | Rewards the model when its answer matches the correct answer |
-| `int_reward_func` | Rewards the model for providing a numeric answer |
-| `strict_format_reward_func` and `soft_format_reward_func` | Reward the model for following the specified format |
-| `xmlcount_reward_func` | Rewards proper XML tag usage and penalizes extra content after the closing tags |
-
-## Training with GRPO
-
-Now we'll set up the GRPO trainer with our model, tokenizer, and reward functions. This part follows the same approach as the [previous exercise](/course/chapter12/5).
-
-```python
-from trl import GRPOConfig, GRPOTrainer
-
-max_prompt_length = 256
-
-training_args = GRPOConfig(
- learning_rate=5e-6,
- adam_beta1=0.9,
- adam_beta2=0.99,
- weight_decay=0.1,
- warmup_ratio=0.1,
- lr_scheduler_type="cosine",
- optim="paged_adamw_8bit",
- logging_steps=1,
- per_device_train_batch_size=1,
- gradient_accumulation_steps=1, # Increase to 4 for smoother training
- num_generations=6, # Decrease if out of memory
- max_prompt_length=max_prompt_length,
- max_completion_length=max_seq_length - max_prompt_length,
- # num_train_epochs = 1, # Set to 1 for a full training run
- max_steps=250,
- save_steps=250,
- max_grad_norm=0.1,
- report_to="none", # Can use Weights & Biases
- output_dir="outputs",
-)
-
-trainer = GRPOTrainer(
- model=model,
- processing_class=tokenizer,
- reward_funcs=[
- xmlcount_reward_func,
- soft_format_reward_func,
- strict_format_reward_func,
- int_reward_func,
- correctness_reward_func,
- ],
- args=training_args,
- train_dataset=dataset,
-)
-```
-
-The `GRPOConfig` sets various hyperparameters for training:
-- `use_vllm`: Enables fast inference with vLLM
-- `learning_rate`: Controls how quickly the model learns
-- `num_generations`: Number of completions to generate for each prompt
-- `max_steps`: Total number of training steps to perform
-
-Now let's start the training:
-
-```python
-trainer.train()
-```
-
-> [!WARNING]
-> Training may take some time. You might not see rewards increase immediately - it can take 150-200 steps before you start seeing improvements. Be patient!
-
-## Testing the Model
-
-After training, let's test our model to see how it performs. First, we'll save the LoRA weights:
-
-```python
-model.save_lora("grpo_saved_lora")
-```
-
-Now, let's test the model with a new question:
-
-```python
-from vllm import SamplingParams
-
-text = tokenizer.apply_chat_template(
- [
- {"role": "system", "content": SYSTEM_PROMPT},
- {"role": "user", "content": "Calculate pi."},
- ],
- tokenize=False,
- add_generation_prompt=True,
-)
-
-sampling_params = SamplingParams(
- temperature=0.8,
- top_p=0.95,
- max_tokens=1024,
-)
-output = (
- model.fast_generate(
- text,
- sampling_params=sampling_params,
- lora_request=model.load_lora("grpo_saved_lora"),
- )[0]
- .outputs[0]
- .text
-)
-
-print(output)
-```
-
-You should see that the model now follows the specified format, showing its reasoning before providing an answer.
-
-## Saving the Model
-
-Unsloth provides several options for saving your fine-tuned model, but we'll focus on the most common.
-
-```python
-# Save to 16-bit precision
-model.save_pretrained_merged("model", tokenizer, save_method="merged_16bit")
-```
-
-## Pushing to Hugging Face Hub
-
-We'll push the model to the Hugging Face Hub using the `push_to_hub_merged` method. This method allows us to push the model in multiple quantization formats.
-```python
-# Push to Hugging Face Hub (requires a token)
-model.push_to_hub_merged(
- "your-username/model-name", tokenizer, save_method="merged_16bit", token="your-token"
-)
-```
-
-Unsloth also supports saving to GGUF format for use with llama.cpp:
-
-```python
-model.push_to_hub_gguf(
- "your-username/model-name",
- tokenizer,
- quantization_method=["q4_k_m", "q8_0", "q5_k_m"],
- token="your-token",
-)
-```
-
-The GGUF files can be used with llama.cpp or UI-based systems like Jan or Open WebUI.
-
-## Conclusion
-
-In this exercise, you've learned how to:
-1. Set up Unsloth for accelerated fine-tuning
-2. Prepare data for GRPO training
-3. Define custom reward functions to guide the model's learning
-4. Train a model using GRPO
-5. Test the fine-tuned model
-6. Save the model in various formats
-
-GRPO is a powerful technique for aligning language models with specific behaviors, and Unsloth makes it accessible even on limited hardware. By combining multiple reward functions, you can guide the model to follow a specific format while also improving its reasoning capabilities.
-
-For more information and resources, check out:
-- [Unsloth Documentation](https://docs.unsloth.ai/)
-- [Unsloth Discord](https://discord.gg/unsloth)
-- [Unsloth GitHub](https://github.com/unslothai/unsloth)
+
+
+# Practical Exercise: GRPO with Unsloth
+
+In this exercise, you'll fine-tune a model with GRPO (Group Relative Policy Optimization) using Unsloth, to improve a model's reasoning capabilities. We covered GRPO in [Chapter 3](/course/chapter3/3).
+
+Unsloth is a library that accelerates LLM fine-tuning, making it possible to train models faster and with less computational resources. Unsloth is plugs into TRL, so we'll build on what we learned in the previous sections, and adapt it for Unsloth specifics.
+
+> [!TIP]
+> This exercise can be run on a free Google Colab T4 GPU. For the best experience, follow along with the notebook linked above and try it out yourself.
+
+## Install dependencies
+
+First, let's install the necessary libraries. We'll need Unsloth for the accelerated fine-tuning and vLLM for fast inference.
+
+```bash
+pip install unsloth vllm
+pip install --upgrade pillow
+```
+
+## Setting up Unsloth
+
+Unsloth provides a class (`FastLanguageModel`) that integrates transformers with Unsloth optimizations. Let's import it:
+
+```python
+from unsloth import FastLanguageModel
+```
+
+Now, let's load Google's Gemma 3 1B Instruct model and configure it for fine-tuning:
+
+```python
+from unsloth import FastLanguageModel
+import torch
+
+max_seq_length = 1024 # Can increase for longer reasoning traces
+lora_rank = 32 # Larger rank = smarter, but slower
+
+model, tokenizer = FastLanguageModel.from_pretrained(
+ model_name="google/gemma-3-1b-it",
+ max_seq_length=max_seq_length,
+ load_in_4bit=True, # False for LoRA 16bit
+ fast_inference=True, # Enable vLLM fast inference
+ max_lora_rank=lora_rank,
+ gpu_memory_utilization=0.6, # Reduce if out of memory
+)
+
+model = FastLanguageModel.get_peft_model(
+ model,
+ r=lora_rank, # Choose any number > 0 ! Suggested 8, 16, 32, 64, 128
+ target_modules=[
+ "q_proj",
+ "k_proj",
+ "v_proj",
+ "o_proj",
+ "gate_proj",
+ "up_proj",
+ "down_proj",
+ ], # Remove QKVO if out of memory
+ lora_alpha=lora_rank,
+ use_gradient_checkpointing="unsloth", # Enable long context finetuning
+ random_state=3407,
+)
+```
+
+This code loads the model in 4-bit quantization to save memory and applies LoRA (Low-Rank Adaptation) for efficient fine-tuning. The `target_modules` parameter specifies which layers of the model to fine-tune, and `use_gradient_checkpointing` enables training with longer contexts.
+
+> [!TIP]
+> We won't cover the details of LoRA in this chapter, but you can learn more in [Chapter 11](/course/chapter11/3).
+
+## Data Preparation
+
+For this exercise, we'll use the GSM8K dataset, which contains grade school math problems. We'll format the data to encourage the model to show its reasoning before providing an answer.
+
+First, we will define the format of the prompts and answers:
+
+```python
+# Define the system prompt that instructs the model to use a specific format
+SYSTEM_PROMPT = """
+Respond in the following format:
+
+...
+
+
+...
+
+"""
+
+XML_COT_FORMAT = """\
+
+{reasoning}
+
+
+{answer}
+
+"""
+```
+
+Now, let's prepare the dataset:
+
+```python
+import re
+from datasets import load_dataset, Dataset
+
+
+# Helper functions to extract answers from different formats
+def extract_xml_answer(text: str) -> str:
+ answer = text.split("")[-1]
+ answer = answer.split("")[0]
+ return answer.strip()
+
+
+def extract_hash_answer(text: str) -> str | None:
+ if "####" not in text:
+ return None
+ return text.split("####")[1].strip()
+
+
+# Function to prepare the GSM8K dataset
+def get_gsm8k_questions(split="train") -> Dataset:
+ data = load_dataset("openai/gsm8k", "main")[split]
+ data = data.map(
+ lambda x: {
+ "prompt": [
+ {"role": "system", "content": SYSTEM_PROMPT},
+ {"role": "user", "content": x["question"]},
+ ],
+ "answer": extract_hash_answer(x["answer"]),
+ }
+ )
+ return data
+
+
+dataset = get_gsm8k_questions()
+```
+
+The dataset is prepared by extracting the answer from the dataset and formatting it as a string.
+
+## Defining Reward Functions
+
+As we discussed in [an earlier page](/course/chapter13/4), GRPO can use reward functions to guide the model's learning based on verifiable criteria like length and formatting.
+
+In this exercise, we'll define several reward functions that encourage different aspects of good reasoning. For example, we'll reward the model for providing an integer answer, and for following the strict format.
+
+```python
+# Reward function that checks if the answer is correct
+def correctness_reward_func(prompts, completions, answer, **kwargs) -> list[float]:
+ responses = [completion[0]["content"] for completion in completions]
+ q = prompts[0][-1]["content"]
+ extracted_responses = [extract_xml_answer(r) for r in responses]
+ print(
+ "-" * 20,
+ f"Question:\n{q}",
+ f"\nAnswer:\n{answer[0]}",
+ f"\nResponse:\n{responses[0]}",
+ f"\nExtracted:\n{extracted_responses[0]}",
+ )
+ return [2.0 if r == a else 0.0 for r, a in zip(extracted_responses, answer)]
+
+
+# Reward function that checks if the answer is an integer
+def int_reward_func(completions, **kwargs) -> list[float]:
+ responses = [completion[0]["content"] for completion in completions]
+ extracted_responses = [extract_xml_answer(r) for r in responses]
+ return [0.5 if r.isdigit() else 0.0 for r in extracted_responses]
+
+
+# Reward function that checks if the completion follows the strict format
+def strict_format_reward_func(completions, **kwargs) -> list[float]:
+ pattern = r"^\n.*?\n\n\n.*?\n\n$"
+ responses = [completion[0]["content"] for completion in completions]
+ matches = [re.match(pattern, r) for r in responses]
+ return [0.5 if match else 0.0 for match in matches]
+
+
+# Reward function that checks if the completion follows a more relaxed format
+def soft_format_reward_func(completions, **kwargs) -> list[float]:
+ pattern = r".*?\s*.*?"
+ responses = [completion[0]["content"] for completion in completions]
+ matches = [re.match(pattern, r) for r in responses]
+ return [0.5 if match else 0.0 for match in matches]
+
+
+# Reward function that counts XML tags and penalizes extra content
+def count_xml(text) -> float:
+ count = 0.0
+ if text.count("\n") == 1:
+ count += 0.125
+ if text.count("\n\n") == 1:
+ count += 0.125
+ if text.count("\n\n") == 1:
+ count += 0.125
+ count -= len(text.split("\n\n")[-1]) * 0.001
+ if text.count("\n") == 1:
+ count += 0.125
+ count -= (len(text.split("\n")[-1]) - 1) * 0.001
+ return count
+
+
+def xmlcount_reward_func(completions, **kwargs) -> list[float]:
+ contents = [completion[0]["content"] for completion in completions]
+ return [count_xml(c) for c in contents]
+```
+
+These reward functions serve different purposes:
+
+| Reward Function | Purpose |
+|-----------------|---------|
+| `correctness_reward_func` | Rewards the model when its answer matches the correct answer |
+| `int_reward_func` | Rewards the model for providing a numeric answer |
+| `strict_format_reward_func` and `soft_format_reward_func` | Reward the model for following the specified format |
+| `xmlcount_reward_func` | Rewards proper XML tag usage and penalizes extra content after the closing tags |
+
+## Training with GRPO
+
+Now we'll set up the GRPO trainer with our model, tokenizer, and reward functions. This part follows the same approach as the [previous exercise](/course/chapter12/5).
+
+```python
+from trl import GRPOConfig, GRPOTrainer
+
+max_prompt_length = 256
+
+training_args = GRPOConfig(
+ learning_rate=5e-6,
+ adam_beta1=0.9,
+ adam_beta2=0.99,
+ weight_decay=0.1,
+ warmup_ratio=0.1,
+ lr_scheduler_type="cosine",
+ optim="paged_adamw_8bit",
+ logging_steps=1,
+ per_device_train_batch_size=1,
+ gradient_accumulation_steps=1, # Increase to 4 for smoother training
+ num_generations=6, # Decrease if out of memory
+ max_prompt_length=max_prompt_length,
+ max_completion_length=max_seq_length - max_prompt_length,
+ # num_train_epochs = 1, # Set to 1 for a full training run
+ max_steps=250,
+ save_steps=250,
+ max_grad_norm=0.1,
+ report_to="none", # Can use Weights & Biases
+ output_dir="outputs",
+)
+
+trainer = GRPOTrainer(
+ model=model,
+ processing_class=tokenizer,
+ reward_funcs=[
+ xmlcount_reward_func,
+ soft_format_reward_func,
+ strict_format_reward_func,
+ int_reward_func,
+ correctness_reward_func,
+ ],
+ args=training_args,
+ train_dataset=dataset,
+)
+```
+
+The `GRPOConfig` sets various hyperparameters for training:
+- `use_vllm`: Enables fast inference with vLLM
+- `learning_rate`: Controls how quickly the model learns
+- `num_generations`: Number of completions to generate for each prompt
+- `max_steps`: Total number of training steps to perform
+
+Now let's start the training:
+
+```python
+trainer.train()
+```
+
+> [!WARNING]
+> Training may take some time. You might not see rewards increase immediately - it can take 150-200 steps before you start seeing improvements. Be patient!
+
+## Testing the Model
+
+After training, let's test our model to see how it performs. First, we'll save the LoRA weights:
+
+```python
+model.save_lora("grpo_saved_lora")
+```
+
+Now, let's test the model with a new question:
+
+```python
+from vllm import SamplingParams
+
+text = tokenizer.apply_chat_template(
+ [
+ {"role": "system", "content": SYSTEM_PROMPT},
+ {"role": "user", "content": "Calculate pi."},
+ ],
+ tokenize=False,
+ add_generation_prompt=True,
+)
+
+sampling_params = SamplingParams(
+ temperature=0.8,
+ top_p=0.95,
+ max_tokens=1024,
+)
+output = (
+ model.fast_generate(
+ text,
+ sampling_params=sampling_params,
+ lora_request=model.load_lora("grpo_saved_lora"),
+ )[0]
+ .outputs[0]
+ .text
+)
+
+print(output)
+```
+
+You should see that the model now follows the specified format, showing its reasoning before providing an answer.
+
+## Saving the Model
+
+Unsloth provides several options for saving your fine-tuned model, but we'll focus on the most common.
+
+```python
+# Save to 16-bit precision
+model.save_pretrained_merged("model", tokenizer, save_method="merged_16bit")
+```
+
+## Pushing to Hugging Face Hub
+
+We'll push the model to the Hugging Face Hub using the `push_to_hub_merged` method. This method allows us to push the model in multiple quantization formats.
+```python
+# Push to Hugging Face Hub (requires a token)
+model.push_to_hub_merged(
+ "your-username/model-name", tokenizer, save_method="merged_16bit", token="your-token"
+)
+```
+
+Unsloth also supports saving to GGUF format for use with llama.cpp:
+
+```python
+model.push_to_hub_gguf(
+ "your-username/model-name",
+ tokenizer,
+ quantization_method=["q4_k_m", "q8_0", "q5_k_m"],
+ token="your-token",
+)
+```
+
+The GGUF files can be used with llama.cpp or UI-based systems like Jan or Open WebUI.
+
+## Conclusion
+
+In this exercise, you've learned how to:
+1. Set up Unsloth for accelerated fine-tuning
+2. Prepare data for GRPO training
+3. Define custom reward functions to guide the model's learning
+4. Train a model using GRPO
+5. Test the fine-tuned model
+6. Save the model in various formats
+
+GRPO is a powerful technique for aligning language models with specific behaviors, and Unsloth makes it accessible even on limited hardware. By combining multiple reward functions, you can guide the model to follow a specific format while also improving its reasoning capabilities.
+
+For more information and resources, check out:
+- [Unsloth Documentation](https://docs.unsloth.ai/)
+- [Unsloth Discord](https://discord.gg/unsloth)
+- [Unsloth GitHub](https://github.com/unslothai/unsloth)
diff --git a/chapters/en/chapter12/7.mdx b/chapters/en/chapter12/7.mdx
index 32dfc2067..47d8514fb 100644
--- a/chapters/en/chapter12/7.mdx
+++ b/chapters/en/chapter12/7.mdx
@@ -1,18 +1,18 @@
-# Coming soon...
-
-This chapter is being run as a live cohort now! If you've finished the material so far, here's what to expect:
-
-## Course Schedule
-
-| Date | Unit |
-|------|------|
-| ~March 7th, 2025~ | ~No-Code Exam and Certification~ |
-| ~March 14th, 2025~ | ~Next Practical Exercise~ |
-| March 21st, 2025 | Interactive code review |
-| April 2025 | More written material on building reasoning models |
-| April 2025 | Live sessions on building Open R1 |
-| April 2025 | Code Exam and Certification |
-
-## Staying Up to Date
-
+# Coming soon...
+
+This chapter is being run as a live cohort now! If you've finished the material so far, here's what to expect:
+
+## Course Schedule
+
+| Date | Unit |
+|------|------|
+| ~March 7th, 2025~ | ~No-Code Exam and Certification~ |
+| ~March 14th, 2025~ | ~Next Practical Exercise~ |
+| March 21st, 2025 | Interactive code review |
+| April 2025 | More written material on building reasoning models |
+| April 2025 | Live sessions on building Open R1 |
+| April 2025 | Code Exam and Certification |
+
+## Staying Up to Date
+
If you want to follow the course, follow the [The Reasoning Course](https://huggingface.co/reasoning-course) and join the [Discord community](https://discord.gg/F3vZujJH)!
\ No newline at end of file
diff --git a/chapters/en/chapter2/1.mdx b/chapters/en/chapter2/1.mdx
index a298aecc8..6f165c333 100644
--- a/chapters/en/chapter2/1.mdx
+++ b/chapters/en/chapter2/1.mdx
@@ -1,24 +1,24 @@
-# Introduction[[introduction]]
-
-
-
-As you saw in [Chapter 1](/course/chapter1), Transformer models are usually very large. With millions to tens of *billions* of parameters, training and deploying these models is a complicated undertaking. Furthermore, with new models being released on a near-daily basis and each having its own implementation, trying them all out is no easy task.
-
-The 🤗 Transformers library was created to solve this problem. Its goal is to provide a single API through which any Transformer model can be loaded, trained, and saved. The library's main features are:
-
-- **Ease of use**: Downloading, loading, and using a state-of-the-art NLP model for inference can be done in just two lines of code.
-- **Flexibility**: At their core, all models are simple PyTorch `nn.Module` classes and can be handled like any other models in their respective machine learning (ML) frameworks.
-- **Simplicity**: Hardly any abstractions are made across the library. The "All in one file" is a core concept: a model's forward pass is entirely defined in a single file, so that the code itself is understandable and hackable.
-
-This last feature makes 🤗 Transformers quite different from other ML libraries. The models are not built on modules
-that are shared across files; instead, each model has its own layers. In addition to making the models more approachable and understandable, this allows you to easily experiment on one model without affecting others.
-
-This chapter will begin with an end-to-end example where we use a model and a tokenizer together to replicate the `pipeline()` function introduced in [Chapter 1](/course/chapter1). Next, we'll discuss the model API: we'll dive into the model and configuration classes, and show you how to load a model and how it processes numerical inputs to output predictions.
-
-Then we'll look at the tokenizer API, which is the other main component of the `pipeline()` function. Tokenizers take care of the first and last processing steps, handling the conversion from text to numerical inputs for the neural network, and the conversion back to text when it is needed. Finally, we'll show you how to handle sending multiple sentences through a model in a prepared batch, then wrap it all up with a closer look at the high-level `tokenizer()` function.
-
-> [!TIP]
+# Introduction[[introduction]]
+
+
+
+As you saw in [Chapter 1](/course/chapter1), Transformer models are usually very large. With millions to tens of *billions* of parameters, training and deploying these models is a complicated undertaking. Furthermore, with new models being released on a near-daily basis and each having its own implementation, trying them all out is no easy task.
+
+The 🤗 Transformers library was created to solve this problem. Its goal is to provide a single API through which any Transformer model can be loaded, trained, and saved. The library's main features are:
+
+- **Ease of use**: Downloading, loading, and using a state-of-the-art NLP model for inference can be done in just two lines of code.
+- **Flexibility**: At their core, all models are simple PyTorch `nn.Module` classes and can be handled like any other models in their respective machine learning (ML) frameworks.
+- **Simplicity**: Hardly any abstractions are made across the library. The "All in one file" is a core concept: a model's forward pass is entirely defined in a single file, so that the code itself is understandable and hackable.
+
+This last feature makes 🤗 Transformers quite different from other ML libraries. The models are not built on modules
+that are shared across files; instead, each model has its own layers. In addition to making the models more approachable and understandable, this allows you to easily experiment on one model without affecting others.
+
+This chapter will begin with an end-to-end example where we use a model and a tokenizer together to replicate the `pipeline()` function introduced in [Chapter 1](/course/chapter1). Next, we'll discuss the model API: we'll dive into the model and configuration classes, and show you how to load a model and how it processes numerical inputs to output predictions.
+
+Then we'll look at the tokenizer API, which is the other main component of the `pipeline()` function. Tokenizers take care of the first and last processing steps, handling the conversion from text to numerical inputs for the neural network, and the conversion back to text when it is needed. Finally, we'll show you how to handle sending multiple sentences through a model in a prepared batch, then wrap it all up with a closer look at the high-level `tokenizer()` function.
+
+> [!TIP]
> ⚠️ In order to benefit from all features available with the Model Hub and 🤗 Transformers, we recommend creating an account.
\ No newline at end of file
diff --git a/chapters/en/chapter2/2.mdx b/chapters/en/chapter2/2.mdx
index b47928a1c..a7170e2dc 100644
--- a/chapters/en/chapter2/2.mdx
+++ b/chapters/en/chapter2/2.mdx
@@ -1,232 +1,232 @@
-
-
-# Behind the pipeline[[behind-the-pipeline]]
-
-
-
-
-
-Let's start with a complete example, taking a look at what happened behind the scenes when we executed the following code in [Chapter 1](/course/chapter1):
-
-```python
-from transformers import pipeline
-
-classifier = pipeline("sentiment-analysis")
-classifier(
- [
- "I've been waiting for a HuggingFace course my whole life.",
- "I hate this so much!",
- ]
-)
-```
-
-and obtained:
-
-```python out
-[{'label': 'POSITIVE', 'score': 0.9598047137260437},
- {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
-```
-
-As we saw in [Chapter 1](/course/chapter1), this pipeline groups together three steps: preprocessing, passing the inputs through the model, and postprocessing:
-
-
-

-

-
-
-Let's quickly go over each of these.
-
-## Preprocessing with a tokenizer[[preprocessing-with-a-tokenizer]]
-
-Like other neural networks, Transformer models can't process raw text directly, so the first step of our pipeline is to convert the text inputs into numbers that the model can make sense of. To do this we use a *tokenizer*, which will be responsible for:
-
-- Splitting the input into words, subwords, or symbols (like punctuation) that are called *tokens*
-- Mapping each token to an integer
-- Adding additional inputs that may be useful to the model
-
-All this preprocessing needs to be done in exactly the same way as when the model was pretrained, so we first need to download that information from the [Model Hub](https://huggingface.co/models). To do this, we use the `AutoTokenizer` class and its `from_pretrained()` method. Using the checkpoint name of our model, it will automatically fetch the data associated with the model's tokenizer and cache it (so it's only downloaded the first time you run the code below).
-
-Since the default checkpoint of the `sentiment-analysis` pipeline is `distilbert-base-uncased-finetuned-sst-2-english` (you can see its model card [here](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)), we run the following:
-
-```python
-from transformers import AutoTokenizer
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-```
-
-Once we have the tokenizer, we can directly pass our sentences to it and we'll get back a dictionary that's ready to feed to our model! The only thing left to do is to convert the list of input IDs to tensors.
-
-You can use 🤗 Transformers without having to worry about which ML framework is used as a backend; it might be PyTorch or Flax for some models. However, Transformer models only accept *tensors* as input. If this is your first time hearing about tensors, you can think of them as NumPy arrays instead. A NumPy array can be a scalar (0D), a vector (1D), a matrix (2D), or have more dimensions. It's effectively a tensor; other ML frameworks' tensors behave similarly, and are usually as simple to instantiate as NumPy arrays.
-
-To specify the type of tensors we want to get back (PyTorch or plain NumPy), we use the `return_tensors` argument:
-
-```python
-raw_inputs = [
- "I've been waiting for a HuggingFace course my whole life.",
- "I hate this so much!",
-]
-inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
-print(inputs)
-```
-
-Don't worry about padding and truncation just yet; we'll explain those later. The main things to remember here are that you can pass one sentence or a list of sentences, as well as specifying the type of tensors you want to get back (if no type is passed, you will get a list of lists as a result).
-
-Here's what the results look like as PyTorch tensors:
-
-```python out
-{
- 'input_ids': tensor([
- [ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102],
- [ 101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0]
- ]),
- 'attention_mask': tensor([
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
- [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
- ])
-}
-```
-
-The output itself is a dictionary containing two keys, `input_ids` and `attention_mask`. `input_ids` contains two rows of integers (one for each sentence) that are the unique identifiers of the tokens in each sentence. We'll explain what the `attention_mask` is later in this chapter.
-
-## Going through the model[[going-through-the-model]]
-
-We can download our pretrained model the same way we did with our tokenizer. 🤗 Transformers provides an `AutoModel` class which also has a `from_pretrained()` method:
-
-```python
-from transformers import AutoModel
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-model = AutoModel.from_pretrained(checkpoint)
-```
-
-In this code snippet, we have downloaded the same checkpoint we used in our pipeline before (it should actually have been cached already) and instantiated a model with it.
-
-This architecture contains only the base Transformer module: given some inputs, it outputs what we'll call *hidden states*, also known as *features*. For each model input, we'll retrieve a high-dimensional vector representing the **contextual understanding of that input by the Transformer model**.
-
-If this doesn't make sense, don't worry about it. We'll explain it all later.
-
-While these hidden states can be useful on their own, they're usually inputs to another part of the model, known as the *head*. In [Chapter 1](/course/chapter1), the different tasks could have been performed with the same architecture, but each of these tasks will have a different head associated with it.
-
-### A high-dimensional vector?[[a-high-dimensional-vector]]
-
-The vector output by the Transformer module is usually large. It generally has three dimensions:
-
-- **Batch size**: The number of sequences processed at a time (2 in our example).
-- **Sequence length**: The length of the numerical representation of the sequence (16 in our example).
-- **Hidden size**: The vector dimension of each model input.
-
-It is said to be "high dimensional" because of the last value. The hidden size can be very large (768 is common for smaller models, and in larger models this can reach 3072 or more).
-
-We can see this if we feed the inputs we preprocessed to our model:
-
-```python
-outputs = model(**inputs)
-print(outputs.last_hidden_state.shape)
-```
-
-```python out
-torch.Size([2, 16, 768])
-```
-
-Note that the outputs of 🤗 Transformers models behave like `namedtuple`s or dictionaries. You can access the elements by attributes (like we did) or by key (`outputs["last_hidden_state"]`), or even by index if you know exactly where the thing you are looking for is (`outputs[0]`).
-
-### Model heads: Making sense out of numbers[[model-heads-making-sense-out-of-numbers]]
-
-The model heads take the high-dimensional vector of hidden states as input and project them onto a different dimension. They are usually composed of one or a few linear layers:
-
-
-

-

-
-
-The output of the Transformer model is sent directly to the model head to be processed.
-
-In this diagram, the model is represented by its embeddings layer and the subsequent layers. The embeddings layer converts each input ID in the tokenized input into a vector that represents the associated token. The subsequent layers manipulate those vectors using the attention mechanism to produce the final representation of the sentences.
-
-There are many different architectures available in 🤗 Transformers, with each one designed around tackling a specific task. Here is a non-exhaustive list:
-
-- `*Model` (retrieve the hidden states)
-- `*ForCausalLM`
-- `*ForMaskedLM`
-- `*ForMultipleChoice`
-- `*ForQuestionAnswering`
-- `*ForSequenceClassification`
-- `*ForTokenClassification`
-- and others 🤗
-
-For our example, we will need a model with a sequence classification head (to be able to classify the sentences as positive or negative). So, we won't actually use the `AutoModel` class, but `AutoModelForSequenceClassification`:
-
-```python
-from transformers import AutoModelForSequenceClassification
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-outputs = model(**inputs)
-```
-
-Now if we look at the shape of our outputs, the dimensionality will be much lower: the model head takes as input the high-dimensional vectors we saw before, and outputs vectors containing two values (one per label):
-
-```python
-print(outputs.logits.shape)
-```
-
-```python out
-torch.Size([2, 2])
-```
-
-Since we have just two sentences and two labels, the result we get from our model is of shape 2 x 2.
-
-## Postprocessing the output[[postprocessing-the-output]]
-
-The values we get as output from our model don't necessarily make sense by themselves. Let's take a look:
-
-```python
-print(outputs.logits)
-```
-
-```python out
-tensor([[-1.5607, 1.6123],
- [ 4.1692, -3.3464]], grad_fn=)
-```
-
-Our model predicted `[-1.5607, 1.6123]` for the first sentence and `[ 4.1692, -3.3464]` for the second one. Those are not probabilities but *logits*, the raw, unnormalized scores outputted by the last layer of the model. To be converted to probabilities, they need to go through a [SoftMax](https://en.wikipedia.org/wiki/Softmax_function) layer (all 🤗 Transformers models output the logits, as the loss function for training will generally fuse the last activation function, such as SoftMax, with the actual loss function, such as cross entropy):
-
-```py
-import torch
-
-predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
-print(predictions)
-```
-
-```python out
-tensor([[4.0195e-02, 9.5980e-01],
- [9.9946e-01, 5.4418e-04]], grad_fn=)
-```
-
-Now we can see that the model predicted `[0.0402, 0.9598]` for the first sentence and `[0.9995, 0.0005]` for the second one. These are recognizable probability scores.
-
-To get the labels corresponding to each position, we can inspect the `id2label` attribute of the model config (more on this in the next section):
-
-```python
-model.config.id2label
-```
-
-```python out
-{0: 'NEGATIVE', 1: 'POSITIVE'}
-```
-
-Now we can conclude that the model predicted the following:
-
-- First sentence: NEGATIVE: 0.0402, POSITIVE: 0.9598
-- Second sentence: NEGATIVE: 0.9995, POSITIVE: 0.0005
-
-We have successfully reproduced the three steps of the pipeline: preprocessing with tokenizers, passing the inputs through the model, and postprocessing! Now let's take some time to dive deeper into each of those steps.
-
-> [!TIP]
-> ✏️ **Try it out!** Choose two (or more) texts of your own and run them through the `sentiment-analysis` pipeline. Then replicate the steps you saw here yourself and check that you obtain the same results!
+
+
+# Behind the pipeline[[behind-the-pipeline]]
+
+
+
+
+
+Let's start with a complete example, taking a look at what happened behind the scenes when we executed the following code in [Chapter 1](/course/chapter1):
+
+```python
+from transformers import pipeline
+
+classifier = pipeline("sentiment-analysis")
+classifier(
+ [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "I hate this so much!",
+ ]
+)
+```
+
+and obtained:
+
+```python out
+[{'label': 'POSITIVE', 'score': 0.9598047137260437},
+ {'label': 'NEGATIVE', 'score': 0.9994558095932007}]
+```
+
+As we saw in [Chapter 1](/course/chapter1), this pipeline groups together three steps: preprocessing, passing the inputs through the model, and postprocessing:
+
+
+

+

+
+
+Let's quickly go over each of these.
+
+## Preprocessing with a tokenizer[[preprocessing-with-a-tokenizer]]
+
+Like other neural networks, Transformer models can't process raw text directly, so the first step of our pipeline is to convert the text inputs into numbers that the model can make sense of. To do this we use a *tokenizer*, which will be responsible for:
+
+- Splitting the input into words, subwords, or symbols (like punctuation) that are called *tokens*
+- Mapping each token to an integer
+- Adding additional inputs that may be useful to the model
+
+All this preprocessing needs to be done in exactly the same way as when the model was pretrained, so we first need to download that information from the [Model Hub](https://huggingface.co/models). To do this, we use the `AutoTokenizer` class and its `from_pretrained()` method. Using the checkpoint name of our model, it will automatically fetch the data associated with the model's tokenizer and cache it (so it's only downloaded the first time you run the code below).
+
+Since the default checkpoint of the `sentiment-analysis` pipeline is `distilbert-base-uncased-finetuned-sst-2-english` (you can see its model card [here](https://huggingface.co/distilbert-base-uncased-finetuned-sst-2-english)), we run the following:
+
+```python
+from transformers import AutoTokenizer
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+
+Once we have the tokenizer, we can directly pass our sentences to it and we'll get back a dictionary that's ready to feed to our model! The only thing left to do is to convert the list of input IDs to tensors.
+
+You can use 🤗 Transformers without having to worry about which ML framework is used as a backend; it might be PyTorch or Flax for some models. However, Transformer models only accept *tensors* as input. If this is your first time hearing about tensors, you can think of them as NumPy arrays instead. A NumPy array can be a scalar (0D), a vector (1D), a matrix (2D), or have more dimensions. It's effectively a tensor; other ML frameworks' tensors behave similarly, and are usually as simple to instantiate as NumPy arrays.
+
+To specify the type of tensors we want to get back (PyTorch or plain NumPy), we use the `return_tensors` argument:
+
+```python
+raw_inputs = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "I hate this so much!",
+]
+inputs = tokenizer(raw_inputs, padding=True, truncation=True, return_tensors="pt")
+print(inputs)
+```
+
+Don't worry about padding and truncation just yet; we'll explain those later. The main things to remember here are that you can pass one sentence or a list of sentences, as well as specifying the type of tensors you want to get back (if no type is passed, you will get a list of lists as a result).
+
+Here's what the results look like as PyTorch tensors:
+
+```python out
+{
+ 'input_ids': tensor([
+ [ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102],
+ [ 101, 1045, 5223, 2023, 2061, 2172, 999, 102, 0, 0, 0, 0, 0, 0, 0, 0]
+ ]),
+ 'attention_mask': tensor([
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1, 1, 1, 1, 0, 0, 0, 0, 0, 0, 0, 0]
+ ])
+}
+```
+
+The output itself is a dictionary containing two keys, `input_ids` and `attention_mask`. `input_ids` contains two rows of integers (one for each sentence) that are the unique identifiers of the tokens in each sentence. We'll explain what the `attention_mask` is later in this chapter.
+
+## Going through the model[[going-through-the-model]]
+
+We can download our pretrained model the same way we did with our tokenizer. 🤗 Transformers provides an `AutoModel` class which also has a `from_pretrained()` method:
+
+```python
+from transformers import AutoModel
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+model = AutoModel.from_pretrained(checkpoint)
+```
+
+In this code snippet, we have downloaded the same checkpoint we used in our pipeline before (it should actually have been cached already) and instantiated a model with it.
+
+This architecture contains only the base Transformer module: given some inputs, it outputs what we'll call *hidden states*, also known as *features*. For each model input, we'll retrieve a high-dimensional vector representing the **contextual understanding of that input by the Transformer model**.
+
+If this doesn't make sense, don't worry about it. We'll explain it all later.
+
+While these hidden states can be useful on their own, they're usually inputs to another part of the model, known as the *head*. In [Chapter 1](/course/chapter1), the different tasks could have been performed with the same architecture, but each of these tasks will have a different head associated with it.
+
+### A high-dimensional vector?[[a-high-dimensional-vector]]
+
+The vector output by the Transformer module is usually large. It generally has three dimensions:
+
+- **Batch size**: The number of sequences processed at a time (2 in our example).
+- **Sequence length**: The length of the numerical representation of the sequence (16 in our example).
+- **Hidden size**: The vector dimension of each model input.
+
+It is said to be "high dimensional" because of the last value. The hidden size can be very large (768 is common for smaller models, and in larger models this can reach 3072 or more).
+
+We can see this if we feed the inputs we preprocessed to our model:
+
+```python
+outputs = model(**inputs)
+print(outputs.last_hidden_state.shape)
+```
+
+```python out
+torch.Size([2, 16, 768])
+```
+
+Note that the outputs of 🤗 Transformers models behave like `namedtuple`s or dictionaries. You can access the elements by attributes (like we did) or by key (`outputs["last_hidden_state"]`), or even by index if you know exactly where the thing you are looking for is (`outputs[0]`).
+
+### Model heads: Making sense out of numbers[[model-heads-making-sense-out-of-numbers]]
+
+The model heads take the high-dimensional vector of hidden states as input and project them onto a different dimension. They are usually composed of one or a few linear layers:
+
+
+

+

+
+
+The output of the Transformer model is sent directly to the model head to be processed.
+
+In this diagram, the model is represented by its embeddings layer and the subsequent layers. The embeddings layer converts each input ID in the tokenized input into a vector that represents the associated token. The subsequent layers manipulate those vectors using the attention mechanism to produce the final representation of the sentences.
+
+There are many different architectures available in 🤗 Transformers, with each one designed around tackling a specific task. Here is a non-exhaustive list:
+
+- `*Model` (retrieve the hidden states)
+- `*ForCausalLM`
+- `*ForMaskedLM`
+- `*ForMultipleChoice`
+- `*ForQuestionAnswering`
+- `*ForSequenceClassification`
+- `*ForTokenClassification`
+- and others 🤗
+
+For our example, we will need a model with a sequence classification head (to be able to classify the sentences as positive or negative). So, we won't actually use the `AutoModel` class, but `AutoModelForSequenceClassification`:
+
+```python
+from transformers import AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+outputs = model(**inputs)
+```
+
+Now if we look at the shape of our outputs, the dimensionality will be much lower: the model head takes as input the high-dimensional vectors we saw before, and outputs vectors containing two values (one per label):
+
+```python
+print(outputs.logits.shape)
+```
+
+```python out
+torch.Size([2, 2])
+```
+
+Since we have just two sentences and two labels, the result we get from our model is of shape 2 x 2.
+
+## Postprocessing the output[[postprocessing-the-output]]
+
+The values we get as output from our model don't necessarily make sense by themselves. Let's take a look:
+
+```python
+print(outputs.logits)
+```
+
+```python out
+tensor([[-1.5607, 1.6123],
+ [ 4.1692, -3.3464]], grad_fn=)
+```
+
+Our model predicted `[-1.5607, 1.6123]` for the first sentence and `[ 4.1692, -3.3464]` for the second one. Those are not probabilities but *logits*, the raw, unnormalized scores outputted by the last layer of the model. To be converted to probabilities, they need to go through a [SoftMax](https://en.wikipedia.org/wiki/Softmax_function) layer (all 🤗 Transformers models output the logits, as the loss function for training will generally fuse the last activation function, such as SoftMax, with the actual loss function, such as cross entropy):
+
+```py
+import torch
+
+predictions = torch.nn.functional.softmax(outputs.logits, dim=-1)
+print(predictions)
+```
+
+```python out
+tensor([[4.0195e-02, 9.5980e-01],
+ [9.9946e-01, 5.4418e-04]], grad_fn=)
+```
+
+Now we can see that the model predicted `[0.0402, 0.9598]` for the first sentence and `[0.9995, 0.0005]` for the second one. These are recognizable probability scores.
+
+To get the labels corresponding to each position, we can inspect the `id2label` attribute of the model config (more on this in the next section):
+
+```python
+model.config.id2label
+```
+
+```python out
+{0: 'NEGATIVE', 1: 'POSITIVE'}
+```
+
+Now we can conclude that the model predicted the following:
+
+- First sentence: NEGATIVE: 0.0402, POSITIVE: 0.9598
+- Second sentence: NEGATIVE: 0.9995, POSITIVE: 0.0005
+
+We have successfully reproduced the three steps of the pipeline: preprocessing with tokenizers, passing the inputs through the model, and postprocessing! Now let's take some time to dive deeper into each of those steps.
+
+> [!TIP]
+> ✏️ **Try it out!** Choose two (or more) texts of your own and run them through the `sentiment-analysis` pipeline. Then replicate the steps you saw here yourself and check that you obtain the same results!
diff --git a/chapters/en/chapter2/3.mdx b/chapters/en/chapter2/3.mdx
index cf6309eb1..f4977386f 100644
--- a/chapters/en/chapter2/3.mdx
+++ b/chapters/en/chapter2/3.mdx
@@ -1,297 +1,297 @@
-
-
-# Models[[the-models]]
-
-
-
-
-
-In this section, we'll take a closer look at creating and using models. We'll use the `AutoModel` class, which is handy when you want to instantiate any model from a checkpoint.
-
-## Creating a Transformer[[creating-a-transformer]]
-
-Let's begin by examining what happens when we instantiate an `AutoModel`:
-
-```py
-from transformers import AutoModel
-
-model = AutoModel.from_pretrained("bert-base-cased")
-```
-
-Similar to the tokenizer, the `from_pretrained()` method will download and cache the model data from the Hugging Face Hub. As mentioned previously, the checkpoint name corresponds to a specific model architecture and weights, in this case a BERT model with a basic architecture (12 layers, 768 hidden size, 12 attention heads) and cased inputs (meaning that the uppercase/lowercase distinction is important). There are many checkpoints available on the Hub — you can explore them [here](https://huggingface.co/models).
-
-The `AutoModel` class and its associates are actually simple wrappers designed to fetch the appropriate model architecture for a given checkpoint. It's an "auto" class meaning it will guess the appropriate model architecture for you and instantiate the correct model class. However, if you know the type of model you want to use, you can use the class that defines its architecture directly:
-
-```py
-from transformers import BertModel
-
-model = BertModel.from_pretrained("bert-base-cased")
-```
-
-## Loading and saving[[loading-and-saving]]
-
-Saving a model is as simple as saving a tokenizer. In fact, the models actually have the same `save_pretrained()` method, which saves the model's weights and architecture configuration:
-
-```py
-model.save_pretrained("directory_on_my_computer")
-```
-
-This will save two files to your disk:
-
-```
-ls directory_on_my_computer
-
-config.json model.safetensors
-```
-
-If you look inside the *config.json* file, you'll see all the necessary attributes needed to build the model architecture. This file also contains some metadata, such as where the checkpoint originated and what 🤗 Transformers version you were using when you last saved the checkpoint.
-
-The *pytorch_model.safetensors* file is known as the state dictionary; it contains all your model's weights. The two files work together: the configuration file is needed to know about the model architecture, while the model weights are the parameters of the model.
-
-To reuse a saved model, use the `from_pretrained()` method again:
-
-```py
-from transformers import AutoModel
-
-model = AutoModel.from_pretrained("directory_on_my_computer")
-```
-
-A wonderful feature of the 🤗 Transformers library is the ability to easily share models and tokenizers with the community. To do this, make sure you have an account on [Hugging Face](https://huggingface.co). If you're using a notebook, you can easily log in with this:
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-Otherwise, at your terminal run:
-
-```bash
-huggingface-cli login
-```
-
-Then you can push the model to the Hub with the `push_to_hub()` method:
-
-```py
-model.push_to_hub("my-awesome-model")
-```
-
-This will upload the model files to the Hub, in a repository under your namespace named *my-awesome-model*. Then, anyone can load your model with the `from_pretrained()` method!
-
-```py
-from transformers import AutoModel
-
-model = AutoModel.from_pretrained("your-username/my-awesome-model")
-```
-
-You can do a lot more with the Hub API:
-- Push a model from a local repository
-- Update specific files without re-uploading everything
-- Add model cards to document the model's abilities, limitations, known biases, etc.
-
-See [the documentation](https://huggingface.co/docs/huggingface_hub/how-to-upstream) for a complete tutorial on this, or check out the advanced [Chapter 4](/course/chapter4).
-
-## Encoding text[[encoding-text]]
-
-Transformer models handle text by turning the inputs into numbers. Here we will look at exactly what happens when your text is processed by the tokenizer. We've already seen in [Chapter 1](/course/chapter1) that tokenizers split the text into tokens and then convert these tokens into numbers. We can see this conversion through a simple tokenizer:
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-
-encoded_input = tokenizer("Hello, I'm a single sentence!")
-print(encoded_input)
-```
-
-```python out
-{'input_ids': [101, 8667, 117, 1000, 1045, 1005, 1049, 2235, 17662, 12172, 1012, 102],
- 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
-```
-
-We get a dictionary with the following fields:
-- input_ids: numerical representations of your tokens
-- token_type_ids: these tell the model which part of the input is sentence A and which is sentence B (discussed more in the next section)
-- attention_mask: this indicates which tokens should be attended to and which should not (discussed more in a bit)
-
-We can decode the input IDs to get back the original text:
-
-```py
-tokenizer.decode(encoded_input["input_ids"])
-```
-
-```python out
-"[CLS] Hello, I'm a single sentence! [SEP]"
-```
-
-You'll notice that the tokenizer has added special tokens — `[CLS]` and `[SEP]` — required by the model. Not all models need special tokens; they're utilized when a model was pretrained with them, in which case the tokenizer needs to add them as that model expects these tokens.
-
-You can encode multiple sentences at once, either by batching them together (we'll discuss this soon) or by passing a list:
-
-```py
-encoded_input = tokenizer("How are you?", "I'm fine, thank you!")
-print(encoded_input)
-```
-
-```python out
-{'input_ids': [[101, 1731, 1132, 1128, 136, 102], [101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]],
- 'token_type_ids': [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
- 'attention_mask': [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]}
-```
-
-Note that when passing multiple sentences, the tokenizer returns a list for each sentence for each dictionary value. We can also ask the tokenizer to return tensors directly from PyTorch:
-
-```py
-encoded_input = tokenizer("How are you?", "I'm fine, thank you!", return_tensors="pt")
-print(encoded_input)
-```
-
-```python out
-{'input_ids': tensor([[ 101, 1731, 1132, 1128, 136, 102],
- [ 101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]]),
- 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
- 'attention_mask': tensor([[1, 1, 1, 1, 1, 1],
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}
-```
-
-But there's a problem: the two lists don't have the same length! Arrays and tensors need to be rectangular, so we can't simply convert these lists to a PyTorch tensor (or NumPy array). The tokenizer provides an option for that: padding.
-
-### Padding inputs[[padding-inputs]]
-
-If we ask the tokenizer to pad the inputs, it will make all sentences the same length by adding a special padding token to the sentences that are shorter than the longest one:
-
-```py
-encoded_input = tokenizer(
- ["How are you?", "I'm fine, thank you!"], padding=True, return_tensors="pt"
-)
-print(encoded_input)
-```
-
-```python out
-{'input_ids': tensor([[ 101, 1731, 1132, 1128, 136, 102, 0, 0, 0, 0],
- [ 101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]]),
- 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
- 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
- [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}
-```
-
-Now we have rectangular tensors! Note that the padding tokens have been encoded into input IDs with ID 0, and they have an attention mask value of 0 as well. This is because those padding tokens shouldn't be analyzed by the model: they're not part of the actual sentence.
-
-### Truncating inputs[[truncating-inputs]]
-
-The tensors might get too big to be processed by the model. For instance, BERT was only pretrained with sequences up to 512 tokens, so it cannot process longer sequences. If you have sequences longer than the model can handle, you'll need to truncate them with the `truncation` parameter:
-
-```py
-encoded_input = tokenizer(
- "This is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long sentence.",
- truncation=True,
-)
-print(encoded_input["input_ids"])
-```
-
-```python out
-[101, 1188, 1110, 170, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1179, 5650, 119, 102]
-```
-
-By combining the padding and truncation arguments, you can make sure your tensors have the exact size you need:
-
-```py
-encoded_input = tokenizer(
- ["How are you?", "I'm fine, thank you!"],
- padding=True,
- truncation=True,
- max_length=5,
- return_tensors="pt",
-)
-print(encoded_input)
-```
-
-```python out
-{'input_ids': tensor([[ 101, 1731, 1132, 1128, 102],
- [ 101, 1045, 1005, 1049, 102]]),
- 'token_type_ids': tensor([[0, 0, 0, 0, 0],
- [0, 0, 0, 0, 0]]),
- 'attention_mask': tensor([[1, 1, 1, 1, 1],
- [1, 1, 1, 1, 1]])}
-```
-
-### Adding special tokens
-
-Special tokens (or at least the concept of them) is particularly important to BERT and derived models. These tokens are added to better represent the sentence boundaries, such as the beginning of a sentence (`[CLS]`) or separator between sentences (`[SEP]`). Let's look at a simple example:
-
-```py
-encoded_input = tokenizer("How are you?")
-print(encoded_input["input_ids"])
-tokenizer.decode(encoded_input["input_ids"])
-```
-
-```python out
-[101, 1731, 1132, 1128, 136, 102]
-'[CLS] How are you? [SEP]'
-```
-
-These special tokens are automatically added by the tokenizer. Not all models need special tokens; they are primarily used when a model was pretrained with them, in which case the tokenizer will add them since the model expects them.
-
-### Why is all of this necessary?
-
-Here's a concrete example. Consider these encoded sequences:
-
-```py
-sequences = [
- "I've been waiting for a HuggingFace course my whole life.",
- "I hate this so much!",
-]
-```
-
-Once tokenized, we have:
-
-```python
-encoded_sequences = [
- [
- 101,
- 1045,
- 1005,
- 2310,
- 2042,
- 3403,
- 2005,
- 1037,
- 17662,
- 12172,
- 2607,
- 2026,
- 2878,
- 2166,
- 1012,
- 102,
- ],
- [101, 1045, 5223, 2023, 2061, 2172, 999, 102],
-]
-```
-
-This is a list of encoded sequences: a list of lists. Tensors only accept rectangular shapes (think matrices). This "array" is already of rectangular shape, so converting it to a tensor is easy:
-
-```py
-import torch
-
-model_inputs = torch.tensor(encoded_sequences)
-```
-
-### Using the tensors as inputs to the model[[using-the-tensors-as-inputs-to-the-model]]
-
-Making use of the tensors with the model is extremely simple — we just call the model with the inputs:
-
-```py
-output = model(model_inputs)
-```
-
-While the model accepts a lot of different arguments, only the input IDs are necessary. We'll explain what the other arguments do and when they are required later,
-but first we need to take a closer look at the tokenizers that build the inputs that a Transformer model can understand.
+
+
+# Models[[the-models]]
+
+
+
+
+
+In this section, we'll take a closer look at creating and using models. We'll use the `AutoModel` class, which is handy when you want to instantiate any model from a checkpoint.
+
+## Creating a Transformer[[creating-a-transformer]]
+
+Let's begin by examining what happens when we instantiate an `AutoModel`:
+
+```py
+from transformers import AutoModel
+
+model = AutoModel.from_pretrained("bert-base-cased")
+```
+
+Similar to the tokenizer, the `from_pretrained()` method will download and cache the model data from the Hugging Face Hub. As mentioned previously, the checkpoint name corresponds to a specific model architecture and weights, in this case a BERT model with a basic architecture (12 layers, 768 hidden size, 12 attention heads) and cased inputs (meaning that the uppercase/lowercase distinction is important). There are many checkpoints available on the Hub — you can explore them [here](https://huggingface.co/models).
+
+The `AutoModel` class and its associates are actually simple wrappers designed to fetch the appropriate model architecture for a given checkpoint. It's an "auto" class meaning it will guess the appropriate model architecture for you and instantiate the correct model class. However, if you know the type of model you want to use, you can use the class that defines its architecture directly:
+
+```py
+from transformers import BertModel
+
+model = BertModel.from_pretrained("bert-base-cased")
+```
+
+## Loading and saving[[loading-and-saving]]
+
+Saving a model is as simple as saving a tokenizer. In fact, the models actually have the same `save_pretrained()` method, which saves the model's weights and architecture configuration:
+
+```py
+model.save_pretrained("directory_on_my_computer")
+```
+
+This will save two files to your disk:
+
+```
+ls directory_on_my_computer
+
+config.json model.safetensors
+```
+
+If you look inside the *config.json* file, you'll see all the necessary attributes needed to build the model architecture. This file also contains some metadata, such as where the checkpoint originated and what 🤗 Transformers version you were using when you last saved the checkpoint.
+
+The *pytorch_model.safetensors* file is known as the state dictionary; it contains all your model's weights. The two files work together: the configuration file is needed to know about the model architecture, while the model weights are the parameters of the model.
+
+To reuse a saved model, use the `from_pretrained()` method again:
+
+```py
+from transformers import AutoModel
+
+model = AutoModel.from_pretrained("directory_on_my_computer")
+```
+
+A wonderful feature of the 🤗 Transformers library is the ability to easily share models and tokenizers with the community. To do this, make sure you have an account on [Hugging Face](https://huggingface.co). If you're using a notebook, you can easily log in with this:
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+Otherwise, at your terminal run:
+
+```bash
+huggingface-cli login
+```
+
+Then you can push the model to the Hub with the `push_to_hub()` method:
+
+```py
+model.push_to_hub("my-awesome-model")
+```
+
+This will upload the model files to the Hub, in a repository under your namespace named *my-awesome-model*. Then, anyone can load your model with the `from_pretrained()` method!
+
+```py
+from transformers import AutoModel
+
+model = AutoModel.from_pretrained("your-username/my-awesome-model")
+```
+
+You can do a lot more with the Hub API:
+- Push a model from a local repository
+- Update specific files without re-uploading everything
+- Add model cards to document the model's abilities, limitations, known biases, etc.
+
+See [the documentation](https://huggingface.co/docs/huggingface_hub/how-to-upstream) for a complete tutorial on this, or check out the advanced [Chapter 4](/course/chapter4).
+
+## Encoding text[[encoding-text]]
+
+Transformer models handle text by turning the inputs into numbers. Here we will look at exactly what happens when your text is processed by the tokenizer. We've already seen in [Chapter 1](/course/chapter1) that tokenizers split the text into tokens and then convert these tokens into numbers. We can see this conversion through a simple tokenizer:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+
+encoded_input = tokenizer("Hello, I'm a single sentence!")
+print(encoded_input)
+```
+
+```python out
+{'input_ids': [101, 8667, 117, 1000, 1045, 1005, 1049, 2235, 17662, 12172, 1012, 102],
+ 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]}
+```
+
+We get a dictionary with the following fields:
+- input_ids: numerical representations of your tokens
+- token_type_ids: these tell the model which part of the input is sentence A and which is sentence B (discussed more in the next section)
+- attention_mask: this indicates which tokens should be attended to and which should not (discussed more in a bit)
+
+We can decode the input IDs to get back the original text:
+
+```py
+tokenizer.decode(encoded_input["input_ids"])
+```
+
+```python out
+"[CLS] Hello, I'm a single sentence! [SEP]"
+```
+
+You'll notice that the tokenizer has added special tokens — `[CLS]` and `[SEP]` — required by the model. Not all models need special tokens; they're utilized when a model was pretrained with them, in which case the tokenizer needs to add them as that model expects these tokens.
+
+You can encode multiple sentences at once, either by batching them together (we'll discuss this soon) or by passing a list:
+
+```py
+encoded_input = tokenizer("How are you?", "I'm fine, thank you!")
+print(encoded_input)
+```
+
+```python out
+{'input_ids': [[101, 1731, 1132, 1128, 136, 102], [101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]],
+ 'token_type_ids': [[0, 0, 0, 0, 0, 0], [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]],
+ 'attention_mask': [[1, 1, 1, 1, 1, 1], [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]]}
+```
+
+Note that when passing multiple sentences, the tokenizer returns a list for each sentence for each dictionary value. We can also ask the tokenizer to return tensors directly from PyTorch:
+
+```py
+encoded_input = tokenizer("How are you?", "I'm fine, thank you!", return_tensors="pt")
+print(encoded_input)
+```
+
+```python out
+{'input_ids': tensor([[ 101, 1731, 1132, 1128, 136, 102],
+ [ 101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]]),
+ 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
+ 'attention_mask': tensor([[1, 1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}
+```
+
+But there's a problem: the two lists don't have the same length! Arrays and tensors need to be rectangular, so we can't simply convert these lists to a PyTorch tensor (or NumPy array). The tokenizer provides an option for that: padding.
+
+### Padding inputs[[padding-inputs]]
+
+If we ask the tokenizer to pad the inputs, it will make all sentences the same length by adding a special padding token to the sentences that are shorter than the longest one:
+
+```py
+encoded_input = tokenizer(
+ ["How are you?", "I'm fine, thank you!"], padding=True, return_tensors="pt"
+)
+print(encoded_input)
+```
+
+```python out
+{'input_ids': tensor([[ 101, 1731, 1132, 1128, 136, 102, 0, 0, 0, 0],
+ [ 101, 1045, 1005, 1049, 2503, 117, 5763, 1128, 136, 102]]),
+ 'token_type_ids': tensor([[0, 0, 0, 0, 0, 0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0, 0, 0, 0, 0, 0]]),
+ 'attention_mask': tensor([[1, 1, 1, 1, 1, 1, 0, 0, 0, 0],
+ [1, 1, 1, 1, 1, 1, 1, 1, 1, 1]])}
+```
+
+Now we have rectangular tensors! Note that the padding tokens have been encoded into input IDs with ID 0, and they have an attention mask value of 0 as well. This is because those padding tokens shouldn't be analyzed by the model: they're not part of the actual sentence.
+
+### Truncating inputs[[truncating-inputs]]
+
+The tensors might get too big to be processed by the model. For instance, BERT was only pretrained with sequences up to 512 tokens, so it cannot process longer sequences. If you have sequences longer than the model can handle, you'll need to truncate them with the `truncation` parameter:
+
+```py
+encoded_input = tokenizer(
+ "This is a very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very very long sentence.",
+ truncation=True,
+)
+print(encoded_input["input_ids"])
+```
+
+```python out
+[101, 1188, 1110, 170, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1505, 1179, 5650, 119, 102]
+```
+
+By combining the padding and truncation arguments, you can make sure your tensors have the exact size you need:
+
+```py
+encoded_input = tokenizer(
+ ["How are you?", "I'm fine, thank you!"],
+ padding=True,
+ truncation=True,
+ max_length=5,
+ return_tensors="pt",
+)
+print(encoded_input)
+```
+
+```python out
+{'input_ids': tensor([[ 101, 1731, 1132, 1128, 102],
+ [ 101, 1045, 1005, 1049, 102]]),
+ 'token_type_ids': tensor([[0, 0, 0, 0, 0],
+ [0, 0, 0, 0, 0]]),
+ 'attention_mask': tensor([[1, 1, 1, 1, 1],
+ [1, 1, 1, 1, 1]])}
+```
+
+### Adding special tokens
+
+Special tokens (or at least the concept of them) is particularly important to BERT and derived models. These tokens are added to better represent the sentence boundaries, such as the beginning of a sentence (`[CLS]`) or separator between sentences (`[SEP]`). Let's look at a simple example:
+
+```py
+encoded_input = tokenizer("How are you?")
+print(encoded_input["input_ids"])
+tokenizer.decode(encoded_input["input_ids"])
+```
+
+```python out
+[101, 1731, 1132, 1128, 136, 102]
+'[CLS] How are you? [SEP]'
+```
+
+These special tokens are automatically added by the tokenizer. Not all models need special tokens; they are primarily used when a model was pretrained with them, in which case the tokenizer will add them since the model expects them.
+
+### Why is all of this necessary?
+
+Here's a concrete example. Consider these encoded sequences:
+
+```py
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "I hate this so much!",
+]
+```
+
+Once tokenized, we have:
+
+```python
+encoded_sequences = [
+ [
+ 101,
+ 1045,
+ 1005,
+ 2310,
+ 2042,
+ 3403,
+ 2005,
+ 1037,
+ 17662,
+ 12172,
+ 2607,
+ 2026,
+ 2878,
+ 2166,
+ 1012,
+ 102,
+ ],
+ [101, 1045, 5223, 2023, 2061, 2172, 999, 102],
+]
+```
+
+This is a list of encoded sequences: a list of lists. Tensors only accept rectangular shapes (think matrices). This "array" is already of rectangular shape, so converting it to a tensor is easy:
+
+```py
+import torch
+
+model_inputs = torch.tensor(encoded_sequences)
+```
+
+### Using the tensors as inputs to the model[[using-the-tensors-as-inputs-to-the-model]]
+
+Making use of the tensors with the model is extremely simple — we just call the model with the inputs:
+
+```py
+output = model(model_inputs)
+```
+
+While the model accepts a lot of different arguments, only the input IDs are necessary. We'll explain what the other arguments do and when they are required later,
+but first we need to take a closer look at the tokenizers that build the inputs that a Transformer model can understand.
diff --git a/chapters/en/chapter2/4.mdx b/chapters/en/chapter2/4.mdx
index 024af4860..0003b64fc 100644
--- a/chapters/en/chapter2/4.mdx
+++ b/chapters/en/chapter2/4.mdx
@@ -1,218 +1,218 @@
-
-
-# Tokenizers[[tokenizers]]
-
-
-
-
-
-Tokenizers are one of the core components of the NLP pipeline. They serve one purpose: to translate text into data that can be processed by the model. Models can only process numbers, so tokenizers need to convert our text inputs to numerical data. In this section, we'll explore exactly what happens in the tokenization pipeline.
-
-In NLP tasks, the data that is generally processed is raw text. Here's an example of such text:
-
-```
-Jim Henson was a puppeteer
-```
-
-However, models can only process numbers, so we need to find a way to convert the raw text to numbers. That's what the tokenizers do, and there are a lot of ways to go about this. The goal is to find the most meaningful representation — that is, the one that makes the most sense to the model — and, if possible, the smallest representation.
-
-Let's take a look at some examples of tokenization algorithms, and try to answer some of the questions you may have about tokenization.
-
-## Word-based[[word-based]]
-
-
-
-The first type of tokenizer that comes to mind is _word-based_. It's generally very easy to set up and use with only a few rules, and it often yields decent results. For example, in the image below, the goal is to split the raw text into words and find a numerical representation for each of them:
-
-
-

-

-
-
-There are different ways to split the text. For example, we could use whitespace to tokenize the text into words by applying Python's `split()` function:
-
-```py
-tokenized_text = "Jim Henson was a puppeteer".split()
-print(tokenized_text)
-```
-
-```python out
-['Jim', 'Henson', 'was', 'a', 'puppeteer']
-```
-
-There are also variations of word tokenizers that have extra rules for punctuation. With this kind of tokenizer, we can end up with some pretty large "vocabularies," where a vocabulary is defined by the total number of independent tokens that we have in our corpus.
-
-Each word gets assigned an ID, starting from 0 and going up to the size of the vocabulary. The model uses these IDs to identify each word.
-
-If we want to completely cover a language with a word-based tokenizer, we'll need to have an identifier for each word in the language, which will generate a huge amount of tokens. For example, there are over 500,000 words in the English language, so to build a map from each word to an input ID we'd need to keep track of that many IDs. Furthermore, words like "dog" are represented differently from words like "dogs", and the model will initially have no way of knowing that "dog" and "dogs" are similar: it will identify the two words as unrelated. The same applies to other similar words, like "run" and "running", which the model will not see as being similar initially.
-
-Finally, we need a custom token to represent words that are not in our vocabulary. This is known as the "unknown" token, often represented as "[UNK]" or "<unk>". It's generally a bad sign if you see that the tokenizer is producing a lot of these tokens, as it wasn't able to retrieve a sensible representation of a word and you're losing information along the way. The goal when crafting the vocabulary is to do it in such a way that the tokenizer tokenizes as few words as possible into the unknown token.
-
-One way to reduce the amount of unknown tokens is to go one level deeper, using a _character-based_ tokenizer.
-
-## Character-based[[character-based]]
-
-
-
-Character-based tokenizers split the text into characters, rather than words. This has two primary benefits:
-
-- The vocabulary is much smaller.
-- There are much fewer out-of-vocabulary (unknown) tokens, since every word can be built from characters.
-
-But here too some questions arise concerning spaces and punctuation:
-
-
-

-

-
-
-This approach isn't perfect either. Since the representation is now based on characters rather than words, one could argue that, intuitively, it's less meaningful: each character doesn't mean a lot on its own, whereas that is the case with words. However, this again differs according to the language; in Chinese, for example, each character carries more information than a character in a Latin language.
-
-Another thing to consider is that we'll end up with a very large amount of tokens to be processed by our model: whereas a word would only be a single token with a word-based tokenizer, it can easily turn into 10 or more tokens when converted into characters.
-
-To get the best of both worlds, we can use a third technique that combines the two approaches: *subword tokenization*.
-
-## Subword tokenization[[subword-tokenization]]
-
-
-
-Subword tokenization algorithms rely on the principle that frequently used words should not be split into smaller subwords, but rare words should be decomposed into meaningful subwords.
-
-For instance, "annoyingly" might be considered a rare word and could be decomposed into "annoying" and "ly". These are both likely to appear more frequently as standalone subwords, while at the same time the meaning of "annoyingly" is kept by the composite meaning of "annoying" and "ly".
-
-Here is an example showing how a subword tokenization algorithm would tokenize the sequence "Let's do tokenization!":
-
-
-

-

-
-
-These subwords end up providing a lot of semantic meaning: for instance, in the example above "tokenization" was split into "token" and "ization", two tokens that have a semantic meaning while being space-efficient (only two tokens are needed to represent a long word). This allows us to have relatively good coverage with small vocabularies, and close to no unknown tokens.
-
-This approach is especially useful in agglutinative languages such as Turkish, where you can form (almost) arbitrarily long complex words by stringing together subwords.
-
-### And more![[and-more]]
-
-Unsurprisingly, there are many more techniques out there. To name a few:
-
-- Byte-level BPE, as used in GPT-2
-- WordPiece, as used in BERT
-- SentencePiece or Unigram, as used in several multilingual models
-
-You should now have sufficient knowledge of how tokenizers work to get started with the API.
-
-## Loading and saving[[loading-and-saving]]
-
-Loading and saving tokenizers is as simple as it is with models. Actually, it's based on the same two methods: `from_pretrained()` and `save_pretrained()`. These methods will load or save the algorithm used by the tokenizer (a bit like the *architecture* of the model) as well as its vocabulary (a bit like the *weights* of the model).
-
-Loading the BERT tokenizer trained with the same checkpoint as BERT is done the same way as loading the model, except we use the `BertTokenizer` class:
-
-```py
-from transformers import BertTokenizer
-
-tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
-```
-
-Similar to `AutoModel`, the `AutoTokenizer` class will grab the proper tokenizer class in the library based on the checkpoint name, and can be used directly with any checkpoint:
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-```
-
-We can now use the tokenizer as shown in the previous section:
-
-```python
-tokenizer("Using a Transformer network is simple")
-```
-
-```python out
-{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
- 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
- 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
-```
-
-Saving a tokenizer is identical to saving a model:
-
-```py
-tokenizer.save_pretrained("directory_on_my_computer")
-```
-
-We'll talk more about `token_type_ids` in [Chapter 3](/course/chapter3), and we'll explain the `attention_mask` key a little later. First, let's see how the `input_ids` are generated. To do this, we'll need to look at the intermediate methods of the tokenizer.
-
-## Encoding[[encoding]]
-
-
-
-Translating text to numbers is known as _encoding_. Encoding is done in a two-step process: the tokenization, followed by the conversion to input IDs.
-
-As we've seen, the first step is to split the text into words (or parts of words, punctuation symbols, etc.), usually called *tokens*. There are multiple rules that can govern that process, which is why we need to instantiate the tokenizer using the name of the model, to make sure we use the same rules that were used when the model was pretrained.
-
-The second step is to convert those tokens into numbers, so we can build a tensor out of them and feed them to the model. To do this, the tokenizer has a *vocabulary*, which is the part we download when we instantiate it with the `from_pretrained()` method. Again, we need to use the same vocabulary used when the model was pretrained.
-
-To get a better understanding of the two steps, we'll explore them separately. Note that we will use some methods that perform parts of the tokenization pipeline separately to show you the intermediate results of those steps, but in practice, you should call the tokenizer directly on your inputs (as shown in the section 2).
-
-### Tokenization[[tokenization]]
-
-The tokenization process is done by the `tokenize()` method of the tokenizer:
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-
-sequence = "Using a Transformer network is simple"
-tokens = tokenizer.tokenize(sequence)
-
-print(tokens)
-```
-
-The output of this method is a list of strings, or tokens:
-
-```python out
-['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']
-```
-
-This tokenizer is a subword tokenizer: it splits the words until it obtains tokens that can be represented by its vocabulary. That's the case here with `transformer`, which is split into two tokens: `transform` and `##er`.
-
-### From tokens to input IDs[[from-tokens-to-input-ids]]
-
-The conversion to input IDs is handled by the `convert_tokens_to_ids()` tokenizer method:
-
-```py
-ids = tokenizer.convert_tokens_to_ids(tokens)
-
-print(ids)
-```
-
-```python out
-[7993, 170, 11303, 1200, 2443, 1110, 3014]
-```
-
-These outputs, once converted to the appropriate framework tensor, can then be used as inputs to a model as seen earlier in this chapter.
-
-> [!TIP]
-> ✏️ **Try it out!** Replicate the two last steps (tokenization and conversion to input IDs) on the input sentences we used in section 2 ("I've been waiting for a HuggingFace course my whole life." and "I hate this so much!"). Check that you get the same input IDs we got earlier!
-
-## Decoding[[decoding]]
-
-*Decoding* is going the other way around: from vocabulary indices, we want to get a string. This can be done with the `decode()` method as follows:
-
-```py
-decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
-print(decoded_string)
-```
-
-```python out
-'Using a Transformer network is simple'
-```
-
-Note that the `decode` method not only converts the indices back to tokens, but also groups together the tokens that were part of the same words to produce a readable sentence. This behavior will be extremely useful when we use models that predict new text (either text generated from a prompt, or for sequence-to-sequence problems like translation or summarization).
-
-By now you should understand the atomic operations a tokenizer can handle: tokenization, conversion to IDs, and converting IDs back to a string. However, we've just scraped the tip of the iceberg. In the following section, we'll take our approach to its limits and take a look at how to overcome them.
+
+
+# Tokenizers[[tokenizers]]
+
+
+
+
+
+Tokenizers are one of the core components of the NLP pipeline. They serve one purpose: to translate text into data that can be processed by the model. Models can only process numbers, so tokenizers need to convert our text inputs to numerical data. In this section, we'll explore exactly what happens in the tokenization pipeline.
+
+In NLP tasks, the data that is generally processed is raw text. Here's an example of such text:
+
+```
+Jim Henson was a puppeteer
+```
+
+However, models can only process numbers, so we need to find a way to convert the raw text to numbers. That's what the tokenizers do, and there are a lot of ways to go about this. The goal is to find the most meaningful representation — that is, the one that makes the most sense to the model — and, if possible, the smallest representation.
+
+Let's take a look at some examples of tokenization algorithms, and try to answer some of the questions you may have about tokenization.
+
+## Word-based[[word-based]]
+
+
+
+The first type of tokenizer that comes to mind is _word-based_. It's generally very easy to set up and use with only a few rules, and it often yields decent results. For example, in the image below, the goal is to split the raw text into words and find a numerical representation for each of them:
+
+
+

+

+
+
+There are different ways to split the text. For example, we could use whitespace to tokenize the text into words by applying Python's `split()` function:
+
+```py
+tokenized_text = "Jim Henson was a puppeteer".split()
+print(tokenized_text)
+```
+
+```python out
+['Jim', 'Henson', 'was', 'a', 'puppeteer']
+```
+
+There are also variations of word tokenizers that have extra rules for punctuation. With this kind of tokenizer, we can end up with some pretty large "vocabularies," where a vocabulary is defined by the total number of independent tokens that we have in our corpus.
+
+Each word gets assigned an ID, starting from 0 and going up to the size of the vocabulary. The model uses these IDs to identify each word.
+
+If we want to completely cover a language with a word-based tokenizer, we'll need to have an identifier for each word in the language, which will generate a huge amount of tokens. For example, there are over 500,000 words in the English language, so to build a map from each word to an input ID we'd need to keep track of that many IDs. Furthermore, words like "dog" are represented differently from words like "dogs", and the model will initially have no way of knowing that "dog" and "dogs" are similar: it will identify the two words as unrelated. The same applies to other similar words, like "run" and "running", which the model will not see as being similar initially.
+
+Finally, we need a custom token to represent words that are not in our vocabulary. This is known as the "unknown" token, often represented as "[UNK]" or "<unk>". It's generally a bad sign if you see that the tokenizer is producing a lot of these tokens, as it wasn't able to retrieve a sensible representation of a word and you're losing information along the way. The goal when crafting the vocabulary is to do it in such a way that the tokenizer tokenizes as few words as possible into the unknown token.
+
+One way to reduce the amount of unknown tokens is to go one level deeper, using a _character-based_ tokenizer.
+
+## Character-based[[character-based]]
+
+
+
+Character-based tokenizers split the text into characters, rather than words. This has two primary benefits:
+
+- The vocabulary is much smaller.
+- There are much fewer out-of-vocabulary (unknown) tokens, since every word can be built from characters.
+
+But here too some questions arise concerning spaces and punctuation:
+
+
+

+

+
+
+This approach isn't perfect either. Since the representation is now based on characters rather than words, one could argue that, intuitively, it's less meaningful: each character doesn't mean a lot on its own, whereas that is the case with words. However, this again differs according to the language; in Chinese, for example, each character carries more information than a character in a Latin language.
+
+Another thing to consider is that we'll end up with a very large amount of tokens to be processed by our model: whereas a word would only be a single token with a word-based tokenizer, it can easily turn into 10 or more tokens when converted into characters.
+
+To get the best of both worlds, we can use a third technique that combines the two approaches: *subword tokenization*.
+
+## Subword tokenization[[subword-tokenization]]
+
+
+
+Subword tokenization algorithms rely on the principle that frequently used words should not be split into smaller subwords, but rare words should be decomposed into meaningful subwords.
+
+For instance, "annoyingly" might be considered a rare word and could be decomposed into "annoying" and "ly". These are both likely to appear more frequently as standalone subwords, while at the same time the meaning of "annoyingly" is kept by the composite meaning of "annoying" and "ly".
+
+Here is an example showing how a subword tokenization algorithm would tokenize the sequence "Let's do tokenization!":
+
+
+

+

+
+
+These subwords end up providing a lot of semantic meaning: for instance, in the example above "tokenization" was split into "token" and "ization", two tokens that have a semantic meaning while being space-efficient (only two tokens are needed to represent a long word). This allows us to have relatively good coverage with small vocabularies, and close to no unknown tokens.
+
+This approach is especially useful in agglutinative languages such as Turkish, where you can form (almost) arbitrarily long complex words by stringing together subwords.
+
+### And more![[and-more]]
+
+Unsurprisingly, there are many more techniques out there. To name a few:
+
+- Byte-level BPE, as used in GPT-2
+- WordPiece, as used in BERT
+- SentencePiece or Unigram, as used in several multilingual models
+
+You should now have sufficient knowledge of how tokenizers work to get started with the API.
+
+## Loading and saving[[loading-and-saving]]
+
+Loading and saving tokenizers is as simple as it is with models. Actually, it's based on the same two methods: `from_pretrained()` and `save_pretrained()`. These methods will load or save the algorithm used by the tokenizer (a bit like the *architecture* of the model) as well as its vocabulary (a bit like the *weights* of the model).
+
+Loading the BERT tokenizer trained with the same checkpoint as BERT is done the same way as loading the model, except we use the `BertTokenizer` class:
+
+```py
+from transformers import BertTokenizer
+
+tokenizer = BertTokenizer.from_pretrained("bert-base-cased")
+```
+
+Similar to `AutoModel`, the `AutoTokenizer` class will grab the proper tokenizer class in the library based on the checkpoint name, and can be used directly with any checkpoint:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+```
+
+We can now use the tokenizer as shown in the previous section:
+
+```python
+tokenizer("Using a Transformer network is simple")
+```
+
+```python out
+{'input_ids': [101, 7993, 170, 11303, 1200, 2443, 1110, 3014, 102],
+ 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 0],
+ 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1]}
+```
+
+Saving a tokenizer is identical to saving a model:
+
+```py
+tokenizer.save_pretrained("directory_on_my_computer")
+```
+
+We'll talk more about `token_type_ids` in [Chapter 3](/course/chapter3), and we'll explain the `attention_mask` key a little later. First, let's see how the `input_ids` are generated. To do this, we'll need to look at the intermediate methods of the tokenizer.
+
+## Encoding[[encoding]]
+
+
+
+Translating text to numbers is known as _encoding_. Encoding is done in a two-step process: the tokenization, followed by the conversion to input IDs.
+
+As we've seen, the first step is to split the text into words (or parts of words, punctuation symbols, etc.), usually called *tokens*. There are multiple rules that can govern that process, which is why we need to instantiate the tokenizer using the name of the model, to make sure we use the same rules that were used when the model was pretrained.
+
+The second step is to convert those tokens into numbers, so we can build a tensor out of them and feed them to the model. To do this, the tokenizer has a *vocabulary*, which is the part we download when we instantiate it with the `from_pretrained()` method. Again, we need to use the same vocabulary used when the model was pretrained.
+
+To get a better understanding of the two steps, we'll explore them separately. Note that we will use some methods that perform parts of the tokenization pipeline separately to show you the intermediate results of those steps, but in practice, you should call the tokenizer directly on your inputs (as shown in the section 2).
+
+### Tokenization[[tokenization]]
+
+The tokenization process is done by the `tokenize()` method of the tokenizer:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+
+sequence = "Using a Transformer network is simple"
+tokens = tokenizer.tokenize(sequence)
+
+print(tokens)
+```
+
+The output of this method is a list of strings, or tokens:
+
+```python out
+['Using', 'a', 'transform', '##er', 'network', 'is', 'simple']
+```
+
+This tokenizer is a subword tokenizer: it splits the words until it obtains tokens that can be represented by its vocabulary. That's the case here with `transformer`, which is split into two tokens: `transform` and `##er`.
+
+### From tokens to input IDs[[from-tokens-to-input-ids]]
+
+The conversion to input IDs is handled by the `convert_tokens_to_ids()` tokenizer method:
+
+```py
+ids = tokenizer.convert_tokens_to_ids(tokens)
+
+print(ids)
+```
+
+```python out
+[7993, 170, 11303, 1200, 2443, 1110, 3014]
+```
+
+These outputs, once converted to the appropriate framework tensor, can then be used as inputs to a model as seen earlier in this chapter.
+
+> [!TIP]
+> ✏️ **Try it out!** Replicate the two last steps (tokenization and conversion to input IDs) on the input sentences we used in section 2 ("I've been waiting for a HuggingFace course my whole life." and "I hate this so much!"). Check that you get the same input IDs we got earlier!
+
+## Decoding[[decoding]]
+
+*Decoding* is going the other way around: from vocabulary indices, we want to get a string. This can be done with the `decode()` method as follows:
+
+```py
+decoded_string = tokenizer.decode([7993, 170, 11303, 1200, 2443, 1110, 3014])
+print(decoded_string)
+```
+
+```python out
+'Using a Transformer network is simple'
+```
+
+Note that the `decode` method not only converts the indices back to tokens, but also groups together the tokens that were part of the same words to produce a readable sentence. This behavior will be extremely useful when we use models that predict new text (either text generated from a prompt, or for sequence-to-sequence problems like translation or summarization).
+
+By now you should understand the atomic operations a tokenizer can handle: tokenization, conversion to IDs, and converting IDs back to a string. However, we've just scraped the tip of the iceberg. In the following section, we'll take our approach to its limits and take a look at how to overcome them.
diff --git a/chapters/en/chapter2/5.mdx b/chapters/en/chapter2/5.mdx
index 8040813e6..9aa5a4d6e 100644
--- a/chapters/en/chapter2/5.mdx
+++ b/chapters/en/chapter2/5.mdx
@@ -1,200 +1,200 @@
-
-
-# Handling multiple sequences[[handling-multiple-sequences]]
-
-
-
-
-
-In the previous section, we explored the simplest of use cases: doing inference on a single sequence of a small length. However, some questions emerge already:
-
-- How do we handle multiple sequences?
-- How do we handle multiple sequences *of different lengths*?
-- Are vocabulary indices the only inputs that allow a model to work well?
-- Is there such a thing as too long a sequence?
-
-Let's see what kinds of problems these questions pose, and how we can solve them using the 🤗 Transformers API.
-
-## Models expect a batch of inputs[[models-expect-a-batch-of-inputs]]
-
-In the previous exercise you saw how sequences get translated into lists of numbers. Let's convert this list of numbers to a tensor and send it to the model:
-
-```py
-import torch
-from transformers import AutoTokenizer, AutoModelForSequenceClassification
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-
-sequence = "I've been waiting for a HuggingFace course my whole life."
-
-tokens = tokenizer.tokenize(sequence)
-ids = tokenizer.convert_tokens_to_ids(tokens)
-input_ids = torch.tensor(ids)
-# This line will fail.
-model(input_ids)
-```
-
-```python out
-IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)
-```
-
-Oh no! Why did this fail? We followed the steps from the pipeline in section 2.
-
-The problem is that we sent a single sequence to the model, whereas 🤗 Transformers models expect multiple sentences by default. Here we tried to do everything the tokenizer did behind the scenes when we applied it to a `sequence`. But if you look closely, you'll see that the tokenizer didn't just convert the list of input IDs into a tensor, it added a dimension on top of it:
-
-```py
-tokenized_inputs = tokenizer(sequence, return_tensors="pt")
-print(tokenized_inputs["input_ids"])
-```
-
-```python out
-tensor([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172,
- 2607, 2026, 2878, 2166, 1012, 102]])
-```
-
-Let's try again and add a new dimension:
-
-```py
-import torch
-from transformers import AutoTokenizer, AutoModelForSequenceClassification
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-
-sequence = "I've been waiting for a HuggingFace course my whole life."
-
-tokens = tokenizer.tokenize(sequence)
-ids = tokenizer.convert_tokens_to_ids(tokens)
-
-input_ids = torch.tensor([ids])
-print("Input IDs:", input_ids)
-
-output = model(input_ids)
-print("Logits:", output.logits)
-```
-
-We print the input IDs as well as the resulting logits — here's the output:
-
-```python out
-Input IDs: [[ 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]]
-Logits: [[-2.7276, 2.8789]]
-```
-
-*Batching* is the act of sending multiple sentences through the model, all at once. If you only have one sentence, you can just build a batch with a single sequence:
-
-```
-batched_ids = [ids, ids]
-```
-
-This is a batch of two identical sequences!
-
-> [!TIP]
-> ✏️ **Try it out!** Convert this `batched_ids` list into a tensor and pass it through your model. Check that you obtain the same logits as before (but twice)!
-
-Batching allows the model to work when you feed it multiple sentences. Using multiple sequences is just as simple as building a batch with a single sequence. There's a second issue, though. When you're trying to batch together two (or more) sentences, they might be of different lengths. If you've ever worked with tensors before, you know that they need to be of rectangular shape, so you won't be able to convert the list of input IDs into a tensor directly. To work around this problem, we usually *pad* the inputs.
-
-## Padding the inputs[[padding-the-inputs]]
-
-The following list of lists cannot be converted to a tensor:
-
-```py no-format
-batched_ids = [
- [200, 200, 200],
- [200, 200]
-]
-```
-
-In order to work around this, we'll use *padding* to make our tensors have a rectangular shape. Padding makes sure all our sentences have the same length by adding a special word called the *padding token* to the sentences with fewer values. For example, if you have 10 sentences with 10 words and 1 sentence with 20 words, padding will ensure all the sentences have 20 words. In our example, the resulting tensor looks like this:
-
-```py no-format
-padding_id = 100
-
-batched_ids = [
- [200, 200, 200],
- [200, 200, padding_id],
-]
-```
-
-The padding token ID can be found in `tokenizer.pad_token_id`. Let's use it and send our two sentences through the model individually and batched together:
-
-```py no-format
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-
-sequence1_ids = [[200, 200, 200]]
-sequence2_ids = [[200, 200]]
-batched_ids = [
- [200, 200, 200],
- [200, 200, tokenizer.pad_token_id],
-]
-
-print(model(torch.tensor(sequence1_ids)).logits)
-print(model(torch.tensor(sequence2_ids)).logits)
-print(model(torch.tensor(batched_ids)).logits)
-```
-
-```python out
-tensor([[ 1.5694, -1.3895]], grad_fn=)
-tensor([[ 0.5803, -0.4125]], grad_fn=)
-tensor([[ 1.5694, -1.3895],
- [ 1.3373, -1.2163]], grad_fn=)
-```
-
-There's something wrong with the logits in our batched predictions: the second row should be the same as the logits for the second sentence, but we've got completely different values!
-
-This is because the key feature of Transformer models is attention layers that *contextualize* each token. These will take into account the padding tokens since they attend to all of the tokens of a sequence. To get the same result when passing individual sentences of different lengths through the model or when passing a batch with the same sentences and padding applied, we need to tell those attention layers to ignore the padding tokens. This is done by using an attention mask.
-
-## Attention masks[[attention-masks]]
-
-*Attention masks* are tensors with the exact same shape as the input IDs tensor, filled with 0s and 1s: 1s indicate the corresponding tokens should be attended to, and 0s indicate the corresponding tokens should not be attended to (i.e., they should be ignored by the attention layers of the model).
-
-Let's complete the previous example with an attention mask:
-
-```py no-format
-batched_ids = [
- [200, 200, 200],
- [200, 200, tokenizer.pad_token_id],
-]
-
-attention_mask = [
- [1, 1, 1],
- [1, 1, 0],
-]
-
-outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask))
-print(outputs.logits)
-```
-
-```python out
-tensor([[ 1.5694, -1.3895],
- [ 0.5803, -0.4125]], grad_fn=)
-```
-
-Now we get the same logits for the second sentence in the batch.
-
-Notice how the last value of the second sequence is a padding ID, which is a 0 value in the attention mask.
-
-> [!TIP]
-> ✏️ **Try it out!** Apply the tokenization manually on the two sentences used in section 2 ("I've been waiting for a HuggingFace course my whole life." and "I hate this so much!"). Pass them through the model and check that you get the same logits as in section 2. Now batch them together using the padding token, then create the proper attention mask. Check that you obtain the same results when going through the model!
-
-## Longer sequences[[longer-sequences]]
-
-With Transformer models, there is a limit to the lengths of the sequences we can pass the models. Most models handle sequences of up to 512 or 1024 tokens, and will crash when asked to process longer sequences. There are two solutions to this problem:
-
-- Use a model with a longer supported sequence length.
-- Truncate your sequences.
-
-Models have different supported sequence lengths, and some specialize in handling very long sequences. [Longformer](https://huggingface.co/docs/transformers/model_doc/longformer) is one example, and another is [LED](https://huggingface.co/docs/transformers/model_doc/led). If you're working on a task that requires very long sequences, we recommend you take a look at those models.
-
-Otherwise, we recommend you truncate your sequences by specifying the `max_sequence_length` parameter:
-
-```py
-sequence = sequence[:max_sequence_length]
-```
+
+
+# Handling multiple sequences[[handling-multiple-sequences]]
+
+
+
+
+
+In the previous section, we explored the simplest of use cases: doing inference on a single sequence of a small length. However, some questions emerge already:
+
+- How do we handle multiple sequences?
+- How do we handle multiple sequences *of different lengths*?
+- Are vocabulary indices the only inputs that allow a model to work well?
+- Is there such a thing as too long a sequence?
+
+Let's see what kinds of problems these questions pose, and how we can solve them using the 🤗 Transformers API.
+
+## Models expect a batch of inputs[[models-expect-a-batch-of-inputs]]
+
+In the previous exercise you saw how sequences get translated into lists of numbers. Let's convert this list of numbers to a tensor and send it to the model:
+
+```py
+import torch
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+input_ids = torch.tensor(ids)
+# This line will fail.
+model(input_ids)
+```
+
+```python out
+IndexError: Dimension out of range (expected to be in range of [-1, 0], but got 1)
+```
+
+Oh no! Why did this fail? We followed the steps from the pipeline in section 2.
+
+The problem is that we sent a single sequence to the model, whereas 🤗 Transformers models expect multiple sentences by default. Here we tried to do everything the tokenizer did behind the scenes when we applied it to a `sequence`. But if you look closely, you'll see that the tokenizer didn't just convert the list of input IDs into a tensor, it added a dimension on top of it:
+
+```py
+tokenized_inputs = tokenizer(sequence, return_tensors="pt")
+print(tokenized_inputs["input_ids"])
+```
+
+```python out
+tensor([[ 101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172,
+ 2607, 2026, 2878, 2166, 1012, 102]])
+```
+
+Let's try again and add a new dimension:
+
+```py
+import torch
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+
+input_ids = torch.tensor([ids])
+print("Input IDs:", input_ids)
+
+output = model(input_ids)
+print("Logits:", output.logits)
+```
+
+We print the input IDs as well as the resulting logits — here's the output:
+
+```python out
+Input IDs: [[ 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]]
+Logits: [[-2.7276, 2.8789]]
+```
+
+*Batching* is the act of sending multiple sentences through the model, all at once. If you only have one sentence, you can just build a batch with a single sequence:
+
+```
+batched_ids = [ids, ids]
+```
+
+This is a batch of two identical sequences!
+
+> [!TIP]
+> ✏️ **Try it out!** Convert this `batched_ids` list into a tensor and pass it through your model. Check that you obtain the same logits as before (but twice)!
+
+Batching allows the model to work when you feed it multiple sentences. Using multiple sequences is just as simple as building a batch with a single sequence. There's a second issue, though. When you're trying to batch together two (or more) sentences, they might be of different lengths. If you've ever worked with tensors before, you know that they need to be of rectangular shape, so you won't be able to convert the list of input IDs into a tensor directly. To work around this problem, we usually *pad* the inputs.
+
+## Padding the inputs[[padding-the-inputs]]
+
+The following list of lists cannot be converted to a tensor:
+
+```py no-format
+batched_ids = [
+ [200, 200, 200],
+ [200, 200]
+]
+```
+
+In order to work around this, we'll use *padding* to make our tensors have a rectangular shape. Padding makes sure all our sentences have the same length by adding a special word called the *padding token* to the sentences with fewer values. For example, if you have 10 sentences with 10 words and 1 sentence with 20 words, padding will ensure all the sentences have 20 words. In our example, the resulting tensor looks like this:
+
+```py no-format
+padding_id = 100
+
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, padding_id],
+]
+```
+
+The padding token ID can be found in `tokenizer.pad_token_id`. Let's use it and send our two sentences through the model individually and batched together:
+
+```py no-format
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+
+sequence1_ids = [[200, 200, 200]]
+sequence2_ids = [[200, 200]]
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, tokenizer.pad_token_id],
+]
+
+print(model(torch.tensor(sequence1_ids)).logits)
+print(model(torch.tensor(sequence2_ids)).logits)
+print(model(torch.tensor(batched_ids)).logits)
+```
+
+```python out
+tensor([[ 1.5694, -1.3895]], grad_fn=)
+tensor([[ 0.5803, -0.4125]], grad_fn=)
+tensor([[ 1.5694, -1.3895],
+ [ 1.3373, -1.2163]], grad_fn=)
+```
+
+There's something wrong with the logits in our batched predictions: the second row should be the same as the logits for the second sentence, but we've got completely different values!
+
+This is because the key feature of Transformer models is attention layers that *contextualize* each token. These will take into account the padding tokens since they attend to all of the tokens of a sequence. To get the same result when passing individual sentences of different lengths through the model or when passing a batch with the same sentences and padding applied, we need to tell those attention layers to ignore the padding tokens. This is done by using an attention mask.
+
+## Attention masks[[attention-masks]]
+
+*Attention masks* are tensors with the exact same shape as the input IDs tensor, filled with 0s and 1s: 1s indicate the corresponding tokens should be attended to, and 0s indicate the corresponding tokens should not be attended to (i.e., they should be ignored by the attention layers of the model).
+
+Let's complete the previous example with an attention mask:
+
+```py no-format
+batched_ids = [
+ [200, 200, 200],
+ [200, 200, tokenizer.pad_token_id],
+]
+
+attention_mask = [
+ [1, 1, 1],
+ [1, 1, 0],
+]
+
+outputs = model(torch.tensor(batched_ids), attention_mask=torch.tensor(attention_mask))
+print(outputs.logits)
+```
+
+```python out
+tensor([[ 1.5694, -1.3895],
+ [ 0.5803, -0.4125]], grad_fn=)
+```
+
+Now we get the same logits for the second sentence in the batch.
+
+Notice how the last value of the second sequence is a padding ID, which is a 0 value in the attention mask.
+
+> [!TIP]
+> ✏️ **Try it out!** Apply the tokenization manually on the two sentences used in section 2 ("I've been waiting for a HuggingFace course my whole life." and "I hate this so much!"). Pass them through the model and check that you get the same logits as in section 2. Now batch them together using the padding token, then create the proper attention mask. Check that you obtain the same results when going through the model!
+
+## Longer sequences[[longer-sequences]]
+
+With Transformer models, there is a limit to the lengths of the sequences we can pass the models. Most models handle sequences of up to 512 or 1024 tokens, and will crash when asked to process longer sequences. There are two solutions to this problem:
+
+- Use a model with a longer supported sequence length.
+- Truncate your sequences.
+
+Models have different supported sequence lengths, and some specialize in handling very long sequences. [Longformer](https://huggingface.co/docs/transformers/model_doc/longformer) is one example, and another is [LED](https://huggingface.co/docs/transformers/model_doc/led). If you're working on a task that requires very long sequences, we recommend you take a look at those models.
+
+Otherwise, we recommend you truncate your sequences by specifying the `max_sequence_length` parameter:
+
+```py
+sequence = sequence[:max_sequence_length]
+```
diff --git a/chapters/en/chapter2/6.mdx b/chapters/en/chapter2/6.mdx
index 3a0dac876..cd08fd222 100644
--- a/chapters/en/chapter2/6.mdx
+++ b/chapters/en/chapter2/6.mdx
@@ -1,133 +1,133 @@
-
-
-# Putting it all together[[putting-it-all-together]]
-
-
-
-In the last few sections, we've been trying our best to do most of the work by hand. We've explored how tokenizers work and looked at tokenization, conversion to input IDs, padding, truncation, and attention masks.
-
-However, as we saw in section 2, the 🤗 Transformers API can handle all of this for us with a high-level function that we'll dive into here. When you call your `tokenizer` directly on the sentence, you get back inputs that are ready to pass through your model:
-
-```py
-from transformers import AutoTokenizer
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-sequence = "I've been waiting for a HuggingFace course my whole life."
-
-model_inputs = tokenizer(sequence)
-```
-
-Here, the `model_inputs` variable contains everything that's necessary for a model to operate well. For DistilBERT, that includes the input IDs as well as the attention mask. Other models that accept additional inputs will also have those output by the `tokenizer` object.
-
-As we'll see in some examples below, this method is very powerful. First, it can tokenize a single sequence:
-
-```py
-sequence = "I've been waiting for a HuggingFace course my whole life."
-
-model_inputs = tokenizer(sequence)
-```
-
-It also handles multiple sequences at a time, with no change in the API:
-
-```py
-sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
-
-model_inputs = tokenizer(sequences)
-```
-
-It can pad according to several objectives:
-
-```py
-# Will pad the sequences up to the maximum sequence length
-model_inputs = tokenizer(sequences, padding="longest")
-
-# Will pad the sequences up to the model max length
-# (512 for BERT or DistilBERT)
-model_inputs = tokenizer(sequences, padding="max_length")
-
-# Will pad the sequences up to the specified max length
-model_inputs = tokenizer(sequences, padding="max_length", max_length=8)
-```
-
-It can also truncate sequences:
-
-```py
-sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
-
-# Will truncate the sequences that are longer than the model max length
-# (512 for BERT or DistilBERT)
-model_inputs = tokenizer(sequences, truncation=True)
-
-# Will truncate the sequences that are longer than the specified max length
-model_inputs = tokenizer(sequences, max_length=8, truncation=True)
-```
-
-The `tokenizer` object can handle the conversion to specific framework tensors, which can then be directly sent to the model. For example, in the following code sample we are prompting the tokenizer to return tensors from the different frameworks — `"pt"` returns PyTorch tensors and `"np"` returns NumPy arrays:
-
-```py
-sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
-
-# Returns PyTorch tensors
-model_inputs = tokenizer(sequences, padding=True, return_tensors="pt")
-
-# Returns NumPy arrays
-model_inputs = tokenizer(sequences, padding=True, return_tensors="np")
-```
-
-## Special tokens[[special-tokens]]
-
-If we take a look at the input IDs returned by the tokenizer, we will see they are a tiny bit different from what we had earlier:
-
-```py
-sequence = "I've been waiting for a HuggingFace course my whole life."
-
-model_inputs = tokenizer(sequence)
-print(model_inputs["input_ids"])
-
-tokens = tokenizer.tokenize(sequence)
-ids = tokenizer.convert_tokens_to_ids(tokens)
-print(ids)
-```
-
-```python out
-[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102]
-[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]
-```
-
-One token ID was added at the beginning, and one at the end. Let's decode the two sequences of IDs above to see what this is about:
-
-```py
-print(tokenizer.decode(model_inputs["input_ids"]))
-print(tokenizer.decode(ids))
-```
-
-```python out
-"[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
-"i've been waiting for a huggingface course my whole life."
-```
-
-The tokenizer added the special word `[CLS]` at the beginning and the special word `[SEP]` at the end. This is because the model was pretrained with those, so to get the same results for inference we need to add them as well. Note that some models don't add special words, or add different ones; models may also add these special words only at the beginning, or only at the end. In any case, the tokenizer knows which ones are expected and will deal with this for you.
-
-## Wrapping up: From tokenizer to model[[wrapping-up-from-tokenizer-to-model]]
-
-Now that we've seen all the individual steps the `tokenizer` object uses when applied on texts, let's see one final time how it can handle multiple sequences (padding!), very long sequences (truncation!), and multiple types of tensors with its main API:
-
-```py
-import torch
-from transformers import AutoTokenizer, AutoModelForSequenceClassification
-
-checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
-
-tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
-output = model(**tokens)
-```
+
+
+# Putting it all together[[putting-it-all-together]]
+
+
+
+In the last few sections, we've been trying our best to do most of the work by hand. We've explored how tokenizers work and looked at tokenization, conversion to input IDs, padding, truncation, and attention masks.
+
+However, as we saw in section 2, the 🤗 Transformers API can handle all of this for us with a high-level function that we'll dive into here. When you call your `tokenizer` directly on the sentence, you get back inputs that are ready to pass through your model:
+
+```py
+from transformers import AutoTokenizer
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+model_inputs = tokenizer(sequence)
+```
+
+Here, the `model_inputs` variable contains everything that's necessary for a model to operate well. For DistilBERT, that includes the input IDs as well as the attention mask. Other models that accept additional inputs will also have those output by the `tokenizer` object.
+
+As we'll see in some examples below, this method is very powerful. First, it can tokenize a single sequence:
+
+```py
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+model_inputs = tokenizer(sequence)
+```
+
+It also handles multiple sequences at a time, with no change in the API:
+
+```py
+sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
+
+model_inputs = tokenizer(sequences)
+```
+
+It can pad according to several objectives:
+
+```py
+# Will pad the sequences up to the maximum sequence length
+model_inputs = tokenizer(sequences, padding="longest")
+
+# Will pad the sequences up to the model max length
+# (512 for BERT or DistilBERT)
+model_inputs = tokenizer(sequences, padding="max_length")
+
+# Will pad the sequences up to the specified max length
+model_inputs = tokenizer(sequences, padding="max_length", max_length=8)
+```
+
+It can also truncate sequences:
+
+```py
+sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
+
+# Will truncate the sequences that are longer than the model max length
+# (512 for BERT or DistilBERT)
+model_inputs = tokenizer(sequences, truncation=True)
+
+# Will truncate the sequences that are longer than the specified max length
+model_inputs = tokenizer(sequences, max_length=8, truncation=True)
+```
+
+The `tokenizer` object can handle the conversion to specific framework tensors, which can then be directly sent to the model. For example, in the following code sample we are prompting the tokenizer to return tensors from the different frameworks — `"pt"` returns PyTorch tensors and `"np"` returns NumPy arrays:
+
+```py
+sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
+
+# Returns PyTorch tensors
+model_inputs = tokenizer(sequences, padding=True, return_tensors="pt")
+
+# Returns NumPy arrays
+model_inputs = tokenizer(sequences, padding=True, return_tensors="np")
+```
+
+## Special tokens[[special-tokens]]
+
+If we take a look at the input IDs returned by the tokenizer, we will see they are a tiny bit different from what we had earlier:
+
+```py
+sequence = "I've been waiting for a HuggingFace course my whole life."
+
+model_inputs = tokenizer(sequence)
+print(model_inputs["input_ids"])
+
+tokens = tokenizer.tokenize(sequence)
+ids = tokenizer.convert_tokens_to_ids(tokens)
+print(ids)
+```
+
+```python out
+[101, 1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012, 102]
+[1045, 1005, 2310, 2042, 3403, 2005, 1037, 17662, 12172, 2607, 2026, 2878, 2166, 1012]
+```
+
+One token ID was added at the beginning, and one at the end. Let's decode the two sequences of IDs above to see what this is about:
+
+```py
+print(tokenizer.decode(model_inputs["input_ids"]))
+print(tokenizer.decode(ids))
+```
+
+```python out
+"[CLS] i've been waiting for a huggingface course my whole life. [SEP]"
+"i've been waiting for a huggingface course my whole life."
+```
+
+The tokenizer added the special word `[CLS]` at the beginning and the special word `[SEP]` at the end. This is because the model was pretrained with those, so to get the same results for inference we need to add them as well. Note that some models don't add special words, or add different ones; models may also add these special words only at the beginning, or only at the end. In any case, the tokenizer knows which ones are expected and will deal with this for you.
+
+## Wrapping up: From tokenizer to model[[wrapping-up-from-tokenizer-to-model]]
+
+Now that we've seen all the individual steps the `tokenizer` object uses when applied on texts, let's see one final time how it can handle multiple sequences (padding!), very long sequences (truncation!), and multiple types of tensors with its main API:
+
+```py
+import torch
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+checkpoint = "distilbert-base-uncased-finetuned-sst-2-english"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+sequences = ["I've been waiting for a HuggingFace course my whole life.", "So have I!"]
+
+tokens = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
+output = model(**tokens)
+```
diff --git a/chapters/en/chapter2/7.mdx b/chapters/en/chapter2/7.mdx
index 657aa28e9..3338bcdad 100644
--- a/chapters/en/chapter2/7.mdx
+++ b/chapters/en/chapter2/7.mdx
@@ -1,18 +1,18 @@
-# Basic usage completed![[basic-usage-completed]]
-
-
-
-Great job following the course up to here! To recap, in this chapter you:
-
-- Learned the basic building blocks of a Transformer model.
-- Learned what makes up a tokenization pipeline.
-- Saw how to use a Transformer model in practice.
-- Learned how to leverage a tokenizer to convert text to tensors that are understandable by the model.
-- Set up a tokenizer and a model together to get from text to predictions.
-- Learned the limitations of input IDs, and learned about attention masks.
-- Played around with versatile and configurable tokenizer methods.
-
-From now on, you should be able to freely navigate the 🤗 Transformers docs: the vocabulary will sound familiar, and you've already seen the methods that you'll use the majority of the time.
+# Basic usage completed![[basic-usage-completed]]
+
+
+
+Great job following the course up to here! To recap, in this chapter you:
+
+- Learned the basic building blocks of a Transformer model.
+- Learned what makes up a tokenization pipeline.
+- Saw how to use a Transformer model in practice.
+- Learned how to leverage a tokenizer to convert text to tensors that are understandable by the model.
+- Set up a tokenizer and a model together to get from text to predictions.
+- Learned the limitations of input IDs, and learned about attention masks.
+- Played around with versatile and configurable tokenizer methods.
+
+From now on, you should be able to freely navigate the 🤗 Transformers docs: the vocabulary will sound familiar, and you've already seen the methods that you'll use the majority of the time.
diff --git a/chapters/en/chapter2/8.mdx b/chapters/en/chapter2/8.mdx
index 84436e81b..78a5a8dcd 100644
--- a/chapters/en/chapter2/8.mdx
+++ b/chapters/en/chapter2/8.mdx
@@ -1,857 +1,857 @@
-# Optimized Inference Deployment
-
-In this section, we'll explore advanced frameworks for optimizing LLM deployments: Text Generation Inference (TGI), vLLM, and llama.cpp. These applications are primarily used in production environments to serve LLMs to users. This section focuses on how to deploy these frameworks in production rather than how to use them for inference on a single machine.
-
-We'll cover how these tools maximize inference efficiency and simplify production deployments of Large Language Models.
-
-## Framework Selection Guide
-
-TGI, vLLM, and llama.cpp serve similar purposes but have distinct characteristics that make them better suited for different use cases. Let's look at the key differences between them, focusing on performance and integration.
-
-### Memory Management and Performance
-
-**TGI** is designed to be stable and predictable in production, using fixed sequence lengths to keep memory usage consistent. TGI manages memory using Flash Attention 2 and continuous batching techniques. This means it can process attention calculations very efficiently and keep the GPU busy by constantly feeding it work. The system can move parts of the model between CPU and GPU when needed, which helps handle larger models.
-
-
-
-
-
-Flash Attention is a technique that optimizes the attention mechanism in transformer models by addressing memory bandwidth bottlenecks. As discussed earlier in [Chapter 1.8](/course/chapter1/8), the attention mechanism has quadratic complexity and memory usage, making it inefficient for long sequences.
-
-The key innovation is in how it manages memory transfers between High Bandwidth Memory (HBM) and faster SRAM cache. Traditional attention repeatedly transfers data between HBM and SRAM, creating bottlenecks by leaving the GPU idle. Flash Attention loads data once into SRAM and performs all calculations there, minimizing expensive memory transfers.
-
-While the benefits are most significant during training, Flash Attention's reduced VRAM usage and improved efficiency make it valuable for inference as well, enabling faster and more scalable LLM serving.
-
-
-
-**vLLM** takes a different approach by using PagedAttention. Just like how a computer manages its memory in pages, vLLM splits the model's memory into smaller blocks. This clever system means it can handle different-sized requests more flexibly and doesn't waste memory space. It's particularly good at sharing memory between different requests and reduces memory fragmentation, which makes the whole system more efficient.
-
-
-
-PagedAttention is a technique that addresses another critical bottleneck in LLM inference: KV cache memory management. As discussed in [Chapter 1.8](/course/chapter1/8), during text generation, the model stores attention keys and values (KV cache) for each generated token to reduce redundant computations. The KV cache can become enormous, especially with long sequences or multiple concurrent requests.
-
-vLLM's key innovation lies in how it manages this cache:
-
-1. **Memory Paging**: Instead of treating the KV cache as one large block, it's divided into fixed-size "pages" (similar to virtual memory in operating systems).
-2. **Non-contiguous Storage**: Pages don't need to be stored contiguously in GPU memory, allowing for more flexible memory allocation.
-3. **Page Table Management**: A page table tracks which pages belong to which sequence, enabling efficient lookup and access.
-4. **Memory Sharing**: For operations like parallel sampling, pages storing the KV cache for the prompt can be shared across multiple sequences.
-
-The PagedAttention approach can lead to up to 24x higher throughput compared to traditional methods, making it a game-changer for production LLM deployments. If you want to go really deep into how PagedAttention works, you can read the [the guide from the vLLM documentation](https://docs.vllm.ai/en/latest/design/kernel/paged_attention.html).
-
-
-
-**llama.cpp** is a highly optimized C/C++ implementation originally designed for running LLaMA models on consumer hardware. It focuses on CPU efficiency with optional GPU acceleration and is ideal for resource-constrained environments. llama.cpp uses quantization techniques to reduce model size and memory requirements while maintaining good performance. It implements optimized kernels for various CPU architectures and supports basic KV cache management for efficient token generation.
-
-
-
-Quantization in llama.cpp reduces the precision of model weights from 32-bit or 16-bit floating point to lower precision formats like 8-bit integers (INT8), 4-bit, or even lower. This significantly reduces memory usage and improves inference speed with minimal quality loss.
-
-Key quantization features in llama.cpp include:
-1. **Multiple Quantization Levels**: Supports 8-bit, 4-bit, 3-bit, and even 2-bit quantization
-2. **GGML/GGUF Format**: Uses custom tensor formats optimized for quantized inference
-3. **Mixed Precision**: Can apply different quantization levels to different parts of the model
-4. **Hardware-Specific Optimizations**: Includes optimized code paths for various CPU architectures (AVX2, AVX-512, NEON)
-
-This approach enables running billion-parameter models on consumer hardware with limited memory, making it perfect for local deployments and edge devices.
-
-
-
-### Deployment and Integration
-
-Let's move on to the deployment and integration differences between the frameworks.
-
-**TGI** excels in enterprise-level deployment with its production-ready features. It comes with built-in Kubernetes support and includes everything you need for running in production, like monitoring through Prometheus and Grafana, automatic scaling, and comprehensive safety features. The system also includes enterprise-grade logging and various protective measures like content filtering and rate limiting to keep your deployment secure and stable.
-
-**vLLM** takes a more flexible, developer-friendly approach to deployment. It's built with Python at its core and can easily replace OpenAI's API in your existing applications. The framework focuses on delivering raw performance and can be customized to fit your specific needs. It works particularly well with Ray for managing clusters, making it a great choice when you need high performance and adaptability.
-
-**llama.cpp** prioritizes simplicity and portability. Its server implementation is lightweight and can run on a wide range of hardware, from powerful servers to consumer laptops and even some high-end mobile devices. With minimal dependencies and a simple C/C++ core, it's easy to deploy in environments where installing Python frameworks would be challenging. The server provides an OpenAI-compatible API while maintaining a much smaller resource footprint than other solutions.
-
-## Getting Started
-
-Let's explore how to use these frameworks for deploying LLMs, starting with installation and basic setup.
-
-### Installation and Basic Setup
-
-
-
-
-
-TGI is easy to install and use, with deep integration into the Hugging Face ecosystem.
-
-First, launch the TGI server using Docker:
-
-```sh
-docker run --gpus all \
- --shm-size 1g \
- -p 8080:80 \
- -v ~/.cache/huggingface:/data \
- ghcr.io/huggingface/text-generation-inference:latest \
- --model-id HuggingFaceTB/SmolLM2-360M-Instruct
-```
-
-Then interact with it using Hugging Face's InferenceClient:
-
-```python
-from huggingface_hub import InferenceClient
-
-# Initialize client pointing to TGI endpoint
-client = InferenceClient(
- model="http://localhost:8080", # URL to the TGI server
-)
-
-# Text generation
-response = client.text_generation(
- "Tell me a story",
- max_new_tokens=100,
- temperature=0.7,
- top_p=0.95,
- details=True,
- stop_sequences=[],
-)
-print(response.generated_text)
-
-# For chat format
-response = client.chat_completion(
- messages=[
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "Tell me a story"},
- ],
- max_tokens=100,
- temperature=0.7,
- top_p=0.95,
-)
-print(response.choices[0].message.content)
-```
-
-Alternatively, you can use the OpenAI client:
-
-```python
-from openai import OpenAI
-
-# Initialize client pointing to TGI endpoint
-client = OpenAI(
- base_url="http://localhost:8080/v1", # Make sure to include /v1
- api_key="not-needed", # TGI doesn't require an API key by default
-)
-
-# Chat completion
-response = client.chat.completions.create(
- model="HuggingFaceTB/SmolLM2-360M-Instruct",
- messages=[
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "Tell me a story"},
- ],
- max_tokens=100,
- temperature=0.7,
- top_p=0.95,
-)
-print(response.choices[0].message.content)
-```
-
-
-
-
-
-llama.cpp is easy to install and use, requiring minimal dependencies and supporting both CPU and GPU inference.
-
-First, install and build llama.cpp:
-
-```sh
-# Clone the repository
-git clone https://github.com/ggerganov/llama.cpp
-cd llama.cpp
-
-# Build the project
-make
-
-# Download the SmolLM2-1.7B-Instruct-GGUF model
-curl -L -O https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct-GGUF/resolve/main/smollm2-1.7b-instruct.Q4_K_M.gguf
-```
-
-Then, launch the server (with OpenAI API compatibility):
-
-```sh
-# Start the server
-./server \
- -m smollm2-1.7b-instruct.Q4_K_M.gguf \
- --host 0.0.0.0 \
- --port 8080 \
- -c 4096 \
- --n-gpu-layers 0 # Set to a higher number to use GPU
-```
-
-Interact with the server using Hugging Face's InferenceClient:
-
-```python
-from huggingface_hub import InferenceClient
-
-# Initialize client pointing to llama.cpp server
-client = InferenceClient(
- model="http://localhost:8080/v1", # URL to the llama.cpp server
- token="sk-no-key-required", # llama.cpp server requires this placeholder
-)
-
-# Text generation
-response = client.text_generation(
- "Tell me a story",
- max_new_tokens=100,
- temperature=0.7,
- top_p=0.95,
- details=True,
-)
-print(response.generated_text)
-
-# For chat format
-response = client.chat_completion(
- messages=[
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "Tell me a story"},
- ],
- max_tokens=100,
- temperature=0.7,
- top_p=0.95,
-)
-print(response.choices[0].message.content)
-```
-
-Alternatively, you can use the OpenAI client:
-
-```python
-from openai import OpenAI
-
-# Initialize client pointing to llama.cpp server
-client = OpenAI(
- base_url="http://localhost:8080/v1",
- api_key="sk-no-key-required", # llama.cpp server requires this placeholder
-)
-
-# Chat completion
-response = client.chat.completions.create(
- model="smollm2-1.7b-instruct", # Model identifier can be anything as server only loads one model
- messages=[
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "Tell me a story"},
- ],
- max_tokens=100,
- temperature=0.7,
- top_p=0.95,
-)
-print(response.choices[0].message.content)
-```
-
-
-
-
-
-vLLM is easy to install and use, with both OpenAI API compatibility and a native Python interface.
-
-First, launch the vLLM OpenAI-compatible server:
-
-```sh
-python -m vllm.entrypoints.openai.api_server \
- --model HuggingFaceTB/SmolLM2-360M-Instruct \
- --host 0.0.0.0 \
- --port 8000
-```
-
-Then interact with it using Hugging Face's InferenceClient:
-
-```python
-from huggingface_hub import InferenceClient
-
-# Initialize client pointing to vLLM endpoint
-client = InferenceClient(
- model="http://localhost:8000/v1", # URL to the vLLM server
-)
-
-# Text generation
-response = client.text_generation(
- "Tell me a story",
- max_new_tokens=100,
- temperature=0.7,
- top_p=0.95,
- details=True,
-)
-print(response.generated_text)
-
-# For chat format
-response = client.chat_completion(
- messages=[
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "Tell me a story"},
- ],
- max_tokens=100,
- temperature=0.7,
- top_p=0.95,
-)
-print(response.choices[0].message.content)
-```
-
-Alternatively, you can use the OpenAI client:
-
-```python
-from openai import OpenAI
-
-# Initialize client pointing to vLLM endpoint
-client = OpenAI(
- base_url="http://localhost:8000/v1",
- api_key="not-needed", # vLLM doesn't require an API key by default
-)
-
-# Chat completion
-response = client.chat.completions.create(
- model="HuggingFaceTB/SmolLM2-360M-Instruct",
- messages=[
- {"role": "system", "content": "You are a helpful assistant."},
- {"role": "user", "content": "Tell me a story"},
- ],
- max_tokens=100,
- temperature=0.7,
- top_p=0.95,
-)
-print(response.choices[0].message.content)
-```
-
-
-
-
-
-### Basic Text Generation
-
-Let's look at examples of text generation with the frameworks:
-
-
-
-
-
-First, deploy TGI with advanced parameters:
-```sh
-docker run --gpus all \
- --shm-size 1g \
- -p 8080:80 \
- -v ~/.cache/huggingface:/data \
- ghcr.io/huggingface/text-generation-inference:latest \
- --model-id HuggingFaceTB/SmolLM2-360M-Instruct \
- --max-total-tokens 4096 \
- --max-input-length 3072 \
- --max-batch-total-tokens 8192 \
- --waiting-served-ratio 1.2
-```
-
-Use the InferenceClient for flexible text generation:
-
-```python
-from huggingface_hub import InferenceClient
-
-client = InferenceClient(model="http://localhost:8080")
-
-# Advanced parameters example
-response = client.chat_completion(
- messages=[
- {"role": "system", "content": "You are a creative storyteller."},
- {"role": "user", "content": "Write a creative story"},
- ],
- temperature=0.8,
- max_tokens=200,
- top_p=0.95,
-)
-print(response.choices[0].message.content)
-
-# Raw text generation
-response = client.text_generation(
- "Write a creative story about space exploration",
- max_new_tokens=200,
- temperature=0.8,
- top_p=0.95,
- repetition_penalty=1.1,
- do_sample=True,
- details=True,
-)
-print(response.generated_text)
-```
-
-Or use the OpenAI client:
-```python
-from openai import OpenAI
-
-client = OpenAI(base_url="http://localhost:8080/v1", api_key="not-needed")
-
-# Advanced parameters example
-response = client.chat.completions.create(
- model="HuggingFaceTB/SmolLM2-360M-Instruct",
- messages=[
- {"role": "system", "content": "You are a creative storyteller."},
- {"role": "user", "content": "Write a creative story"},
- ],
- temperature=0.8, # Higher for more creativity
-)
-print(response.choices[0].message.content)
-```
-
-
-
-
-
-For llama.cpp, you can set advanced parameters when launching the server:
-
-```sh
-./server \
- -m smollm2-1.7b-instruct.Q4_K_M.gguf \
- --host 0.0.0.0 \
- --port 8080 \
- -c 4096 \ # Context size
- --threads 8 \ # CPU threads to use
- --batch-size 512 \ # Batch size for prompt evaluation
- --n-gpu-layers 0 # GPU layers (0 = CPU only)
-```
-
-Use the InferenceClient:
-
-```python
-from huggingface_hub import InferenceClient
-
-client = InferenceClient(model="http://localhost:8080/v1", token="sk-no-key-required")
-
-# Advanced parameters example
-response = client.chat_completion(
- messages=[
- {"role": "system", "content": "You are a creative storyteller."},
- {"role": "user", "content": "Write a creative story"},
- ],
- temperature=0.8,
- max_tokens=200,
- top_p=0.95,
-)
-print(response.choices[0].message.content)
-
-# For direct text generation
-response = client.text_generation(
- "Write a creative story about space exploration",
- max_new_tokens=200,
- temperature=0.8,
- top_p=0.95,
- repetition_penalty=1.1,
- details=True,
-)
-print(response.generated_text)
-```
-
-Or use the OpenAI client for generation with control over the sampling parameters:
-
-```python
-from openai import OpenAI
-
-client = OpenAI(base_url="http://localhost:8080/v1", api_key="sk-no-key-required")
-
-# Advanced parameters example
-response = client.chat.completions.create(
- model="smollm2-1.7b-instruct",
- messages=[
- {"role": "system", "content": "You are a creative storyteller."},
- {"role": "user", "content": "Write a creative story"},
- ],
- temperature=0.8, # Higher for more creativity
- top_p=0.95, # Nucleus sampling probability
- frequency_penalty=0.5, # Reduce repetition of frequent tokens
- presence_penalty=0.5, # Reduce repetition by penalizing tokens already present
- max_tokens=200, # Maximum generation length
-)
-print(response.choices[0].message.content)
-```
-
-You can also use llama.cpp's native library for even more control:
-
-```python
-# Using llama-cpp-python package for direct model access
-from llama_cpp import Llama
-
-# Load the model
-llm = Llama(
- model_path="smollm2-1.7b-instruct.Q4_K_M.gguf",
- n_ctx=4096, # Context window size
- n_threads=8, # CPU threads
- n_gpu_layers=0, # GPU layers (0 = CPU only)
-)
-
-# Format prompt according to the model's expected format
-prompt = """<|im_start|>system
-You are a creative storyteller.
-<|im_end|>
-<|im_start|>user
-Write a creative story
-<|im_end|>
-<|im_start|>assistant
-"""
-
-# Generate response with precise parameter control
-output = llm(
- prompt,
- max_tokens=200,
- temperature=0.8,
- top_p=0.95,
- frequency_penalty=0.5,
- presence_penalty=0.5,
- stop=["<|im_end|>"],
-)
-
-print(output["choices"][0]["text"])
-```
-
-
-
-
-
-For advanced usage with vLLM, you can use the InferenceClient:
-
-```python
-from huggingface_hub import InferenceClient
-
-client = InferenceClient(model="http://localhost:8000/v1")
-
-# Advanced parameters example
-response = client.chat_completion(
- messages=[
- {"role": "system", "content": "You are a creative storyteller."},
- {"role": "user", "content": "Write a creative story"},
- ],
- temperature=0.8,
- max_tokens=200,
- top_p=0.95,
-)
-print(response.choices[0].message.content)
-
-# For direct text generation
-response = client.text_generation(
- "Write a creative story about space exploration",
- max_new_tokens=200,
- temperature=0.8,
- top_p=0.95,
- details=True,
-)
-print(response.generated_text)
-```
-
-You can also use the OpenAI client:
-
-```python
-from openai import OpenAI
-
-client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")
-
-# Advanced parameters example
-response = client.chat.completions.create(
- model="HuggingFaceTB/SmolLM2-360M-Instruct",
- messages=[
- {"role": "system", "content": "You are a creative storyteller."},
- {"role": "user", "content": "Write a creative story"},
- ],
- temperature=0.8,
- top_p=0.95,
- max_tokens=200,
-)
-print(response.choices[0].message.content)
-```
-
-vLLM also provides a native Python interface with fine-grained control:
-
-```python
-from vllm import LLM, SamplingParams
-
-# Initialize the model with advanced parameters
-llm = LLM(
- model="HuggingFaceTB/SmolLM2-360M-Instruct",
- gpu_memory_utilization=0.85,
- max_num_batched_tokens=8192,
- max_num_seqs=256,
- block_size=16,
-)
-
-# Configure sampling parameters
-sampling_params = SamplingParams(
- temperature=0.8, # Higher for more creativity
- top_p=0.95, # Consider top 95% probability mass
- max_tokens=100, # Maximum length
- presence_penalty=1.1, # Reduce repetition
- frequency_penalty=1.1, # Reduce repetition
- stop=["\n\n", "###"], # Stop sequences
-)
-
-# Generate text
-prompt = "Write a creative story"
-outputs = llm.generate(prompt, sampling_params)
-print(outputs[0].outputs[0].text)
-
-# For chat-style interactions
-chat_prompt = [
- {"role": "system", "content": "You are a creative storyteller."},
- {"role": "user", "content": "Write a creative story"},
-]
-formatted_prompt = llm.get_chat_template()(chat_prompt) # Uses model's chat template
-outputs = llm.generate(formatted_prompt, sampling_params)
-print(outputs[0].outputs[0].text)
-```
-
-
-
-
-
-## Advanced Generation Control
-
-### Token Selection and Sampling
-
-The process of generating text involves selecting the next token at each step. This selection process can be controlled through various parameters:
-
-1. **Raw Logits**: The initial output probabilities for each token
-2. **Temperature**: Controls randomness in selection (higher = more creative)
-3. **Top-p (Nucleus) Sampling**: Filters to top tokens making up X% of probability mass
-4. **Top-k Filtering**: Limits selection to k most likely tokens
-
-Here's how to configure these parameters:
-
-
-
-
-
-```python
-client.generate(
- "Write a creative story",
- temperature=0.8, # Higher for more creativity
- top_p=0.95, # Consider top 95% probability mass
- top_k=50, # Consider top 50 tokens
- max_new_tokens=100, # Maximum length
- repetition_penalty=1.1, # Reduce repetition
-)
-```
-
-
-
-
-
-```python
-# Via OpenAI API compatibility
-response = client.completions.create(
- model="smollm2-1.7b-instruct", # Model name (can be any string for llama.cpp server)
- prompt="Write a creative story",
- temperature=0.8, # Higher for more creativity
- top_p=0.95, # Consider top 95% probability mass
- frequency_penalty=1.1, # Reduce repetition
- presence_penalty=0.1, # Reduce repetition
- max_tokens=100, # Maximum length
-)
-
-# Via llama-cpp-python direct access
-output = llm(
- "Write a creative story",
- temperature=0.8,
- top_p=0.95,
- top_k=50,
- max_tokens=100,
- repeat_penalty=1.1,
-)
-```
-
-
-
-
-
-```python
-params = SamplingParams(
- temperature=0.8, # Higher for more creativity
- top_p=0.95, # Consider top 95% probability mass
- top_k=50, # Consider top 50 tokens
- max_tokens=100, # Maximum length
- presence_penalty=0.1, # Reduce repetition
-)
-llm.generate("Write a creative story", sampling_params=params)
-```
-
-
-
-
-
-### Controlling Repetition
-
-Both frameworks provide ways to prevent repetitive text generation:
-
-
-
-
-
-```python
-client.generate(
- "Write a varied text",
- repetition_penalty=1.1, # Penalize repeated tokens
- no_repeat_ngram_size=3, # Prevent 3-gram repetition
-)
-```
-
-
-
-
-
-```python
-# Via OpenAI API
-response = client.completions.create(
- model="smollm2-1.7b-instruct",
- prompt="Write a varied text",
- frequency_penalty=1.1, # Penalize frequent tokens
- presence_penalty=0.8, # Penalize tokens already present
-)
-
-# Via direct library
-output = llm(
- "Write a varied text",
- repeat_penalty=1.1, # Penalize repeated tokens
- frequency_penalty=0.5, # Additional frequency penalty
- presence_penalty=0.5, # Additional presence penalty
-)
-```
-
-
-
-
-
-```python
-params = SamplingParams(
- presence_penalty=0.1, # Penalize token presence
- frequency_penalty=0.1, # Penalize token frequency
-)
-```
-
-
-
-
-
-### Length Control and Stop Sequences
-
-You can control generation length and specify when to stop:
-
-
-
-
-
-```python
-client.generate(
- "Generate a short paragraph",
- max_new_tokens=100,
- min_new_tokens=10,
- stop_sequences=["\n\n", "###"],
-)
-```
-
-
-
-
-
-```python
-# Via OpenAI API
-response = client.completions.create(
- model="smollm2-1.7b-instruct",
- prompt="Generate a short paragraph",
- max_tokens=100,
- stop=["\n\n", "###"],
-)
-
-# Via direct library
-output = llm("Generate a short paragraph", max_tokens=100, stop=["\n\n", "###"])
-```
-
-
-
-
-
-```python
-params = SamplingParams(
- max_tokens=100,
- min_tokens=10,
- stop=["###", "\n\n"],
- ignore_eos=False,
- skip_special_tokens=True,
-)
-```
-
-
-
-
-
-## Memory Management
-
-Both frameworks implement advanced memory management techniques for efficient inference.
-
-
-
-
-
-TGI uses Flash Attention 2 and continuous batching:
-
-```sh
-# Docker deployment with memory optimization
-docker run --gpus all -p 8080:80 \
- --shm-size 1g \
- ghcr.io/huggingface/text-generation-inference:latest \
- --model-id HuggingFaceTB/SmolLM2-1.7B-Instruct \
- --max-batch-total-tokens 8192 \
- --max-input-length 4096
-```
-
-
-
-
-
-llama.cpp uses quantization and optimized memory layout:
-
-```sh
-# Server with memory optimizations
-./server \
- -m smollm2-1.7b-instruct.Q4_K_M.gguf \
- --host 0.0.0.0 \
- --port 8080 \
- -c 2048 \ # Context size
- --threads 4 \ # CPU threads
- --n-gpu-layers 32 \ # Use more GPU layers for larger models
- --mlock \ # Lock memory to prevent swapping
- --cont-batching # Enable continuous batching
-```
-
-For models too large for your GPU, you can use CPU offloading:
-
-```sh
-./server \
- -m smollm2-1.7b-instruct.Q4_K_M.gguf \
- --n-gpu-layers 20 \ # Keep first 20 layers on GPU
- --threads 8 # Use more CPU threads for CPU layers
-```
-
-
-
-
-
-vLLM uses PagedAttention for optimal memory management:
-
-```python
-from vllm.engine.arg_utils import AsyncEngineArgs
-
-engine_args = AsyncEngineArgs(
- model="HuggingFaceTB/SmolLM2-1.7B-Instruct",
- gpu_memory_utilization=0.85,
- max_num_batched_tokens=8192,
- block_size=16,
-)
-
-llm = LLM(engine_args=engine_args)
-```
-
-
-
-
-
-## Resources
-
-- [Text Generation Inference Documentation](https://huggingface.co/docs/text-generation-inference)
-- [TGI GitHub Repository](https://github.com/huggingface/text-generation-inference)
-- [vLLM Documentation](https://vllm.readthedocs.io/)
-- [vLLM GitHub Repository](https://github.com/vllm-project/vllm)
-- [PagedAttention Paper](https://arxiv.org/abs/2309.06180)
-- [llama.cpp GitHub Repository](https://github.com/ggerganov/llama.cpp)
-- [llama-cpp-python Repository](https://github.com/abetlen/llama-cpp-python)
+# Optimized Inference Deployment
+
+In this section, we'll explore advanced frameworks for optimizing LLM deployments: Text Generation Inference (TGI), vLLM, and llama.cpp. These applications are primarily used in production environments to serve LLMs to users. This section focuses on how to deploy these frameworks in production rather than how to use them for inference on a single machine.
+
+We'll cover how these tools maximize inference efficiency and simplify production deployments of Large Language Models.
+
+## Framework Selection Guide
+
+TGI, vLLM, and llama.cpp serve similar purposes but have distinct characteristics that make them better suited for different use cases. Let's look at the key differences between them, focusing on performance and integration.
+
+### Memory Management and Performance
+
+**TGI** is designed to be stable and predictable in production, using fixed sequence lengths to keep memory usage consistent. TGI manages memory using Flash Attention 2 and continuous batching techniques. This means it can process attention calculations very efficiently and keep the GPU busy by constantly feeding it work. The system can move parts of the model between CPU and GPU when needed, which helps handle larger models.
+
+
+
+
+
+Flash Attention is a technique that optimizes the attention mechanism in transformer models by addressing memory bandwidth bottlenecks. As discussed earlier in [Chapter 1.8](/course/chapter1/8), the attention mechanism has quadratic complexity and memory usage, making it inefficient for long sequences.
+
+The key innovation is in how it manages memory transfers between High Bandwidth Memory (HBM) and faster SRAM cache. Traditional attention repeatedly transfers data between HBM and SRAM, creating bottlenecks by leaving the GPU idle. Flash Attention loads data once into SRAM and performs all calculations there, minimizing expensive memory transfers.
+
+While the benefits are most significant during training, Flash Attention's reduced VRAM usage and improved efficiency make it valuable for inference as well, enabling faster and more scalable LLM serving.
+
+
+
+**vLLM** takes a different approach by using PagedAttention. Just like how a computer manages its memory in pages, vLLM splits the model's memory into smaller blocks. This clever system means it can handle different-sized requests more flexibly and doesn't waste memory space. It's particularly good at sharing memory between different requests and reduces memory fragmentation, which makes the whole system more efficient.
+
+
+
+PagedAttention is a technique that addresses another critical bottleneck in LLM inference: KV cache memory management. As discussed in [Chapter 1.8](/course/chapter1/8), during text generation, the model stores attention keys and values (KV cache) for each generated token to reduce redundant computations. The KV cache can become enormous, especially with long sequences or multiple concurrent requests.
+
+vLLM's key innovation lies in how it manages this cache:
+
+1. **Memory Paging**: Instead of treating the KV cache as one large block, it's divided into fixed-size "pages" (similar to virtual memory in operating systems).
+2. **Non-contiguous Storage**: Pages don't need to be stored contiguously in GPU memory, allowing for more flexible memory allocation.
+3. **Page Table Management**: A page table tracks which pages belong to which sequence, enabling efficient lookup and access.
+4. **Memory Sharing**: For operations like parallel sampling, pages storing the KV cache for the prompt can be shared across multiple sequences.
+
+The PagedAttention approach can lead to up to 24x higher throughput compared to traditional methods, making it a game-changer for production LLM deployments. If you want to go really deep into how PagedAttention works, you can read the [the guide from the vLLM documentation](https://docs.vllm.ai/en/latest/design/kernel/paged_attention.html).
+
+
+
+**llama.cpp** is a highly optimized C/C++ implementation originally designed for running LLaMA models on consumer hardware. It focuses on CPU efficiency with optional GPU acceleration and is ideal for resource-constrained environments. llama.cpp uses quantization techniques to reduce model size and memory requirements while maintaining good performance. It implements optimized kernels for various CPU architectures and supports basic KV cache management for efficient token generation.
+
+
+
+Quantization in llama.cpp reduces the precision of model weights from 32-bit or 16-bit floating point to lower precision formats like 8-bit integers (INT8), 4-bit, or even lower. This significantly reduces memory usage and improves inference speed with minimal quality loss.
+
+Key quantization features in llama.cpp include:
+1. **Multiple Quantization Levels**: Supports 8-bit, 4-bit, 3-bit, and even 2-bit quantization
+2. **GGML/GGUF Format**: Uses custom tensor formats optimized for quantized inference
+3. **Mixed Precision**: Can apply different quantization levels to different parts of the model
+4. **Hardware-Specific Optimizations**: Includes optimized code paths for various CPU architectures (AVX2, AVX-512, NEON)
+
+This approach enables running billion-parameter models on consumer hardware with limited memory, making it perfect for local deployments and edge devices.
+
+
+
+### Deployment and Integration
+
+Let's move on to the deployment and integration differences between the frameworks.
+
+**TGI** excels in enterprise-level deployment with its production-ready features. It comes with built-in Kubernetes support and includes everything you need for running in production, like monitoring through Prometheus and Grafana, automatic scaling, and comprehensive safety features. The system also includes enterprise-grade logging and various protective measures like content filtering and rate limiting to keep your deployment secure and stable.
+
+**vLLM** takes a more flexible, developer-friendly approach to deployment. It's built with Python at its core and can easily replace OpenAI's API in your existing applications. The framework focuses on delivering raw performance and can be customized to fit your specific needs. It works particularly well with Ray for managing clusters, making it a great choice when you need high performance and adaptability.
+
+**llama.cpp** prioritizes simplicity and portability. Its server implementation is lightweight and can run on a wide range of hardware, from powerful servers to consumer laptops and even some high-end mobile devices. With minimal dependencies and a simple C/C++ core, it's easy to deploy in environments where installing Python frameworks would be challenging. The server provides an OpenAI-compatible API while maintaining a much smaller resource footprint than other solutions.
+
+## Getting Started
+
+Let's explore how to use these frameworks for deploying LLMs, starting with installation and basic setup.
+
+### Installation and Basic Setup
+
+
+
+
+
+TGI is easy to install and use, with deep integration into the Hugging Face ecosystem.
+
+First, launch the TGI server using Docker:
+
+```sh
+docker run --gpus all \
+ --shm-size 1g \
+ -p 8080:80 \
+ -v ~/.cache/huggingface:/data \
+ ghcr.io/huggingface/text-generation-inference:latest \
+ --model-id HuggingFaceTB/SmolLM2-360M-Instruct
+```
+
+Then interact with it using Hugging Face's InferenceClient:
+
+```python
+from huggingface_hub import InferenceClient
+
+# Initialize client pointing to TGI endpoint
+client = InferenceClient(
+ model="http://localhost:8080", # URL to the TGI server
+)
+
+# Text generation
+response = client.text_generation(
+ "Tell me a story",
+ max_new_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+ details=True,
+ stop_sequences=[],
+)
+print(response.generated_text)
+
+# For chat format
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": "Tell me a story"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+Alternatively, you can use the OpenAI client:
+
+```python
+from openai import OpenAI
+
+# Initialize client pointing to TGI endpoint
+client = OpenAI(
+ base_url="http://localhost:8080/v1", # Make sure to include /v1
+ api_key="not-needed", # TGI doesn't require an API key by default
+)
+
+# Chat completion
+response = client.chat.completions.create(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ messages=[
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": "Tell me a story"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+
+
+
+
+llama.cpp is easy to install and use, requiring minimal dependencies and supporting both CPU and GPU inference.
+
+First, install and build llama.cpp:
+
+```sh
+# Clone the repository
+git clone https://github.com/ggerganov/llama.cpp
+cd llama.cpp
+
+# Build the project
+make
+
+# Download the SmolLM2-1.7B-Instruct-GGUF model
+curl -L -O https://huggingface.co/HuggingFaceTB/SmolLM2-1.7B-Instruct-GGUF/resolve/main/smollm2-1.7b-instruct.Q4_K_M.gguf
+```
+
+Then, launch the server (with OpenAI API compatibility):
+
+```sh
+# Start the server
+./server \
+ -m smollm2-1.7b-instruct.Q4_K_M.gguf \
+ --host 0.0.0.0 \
+ --port 8080 \
+ -c 4096 \
+ --n-gpu-layers 0 # Set to a higher number to use GPU
+```
+
+Interact with the server using Hugging Face's InferenceClient:
+
+```python
+from huggingface_hub import InferenceClient
+
+# Initialize client pointing to llama.cpp server
+client = InferenceClient(
+ model="http://localhost:8080/v1", # URL to the llama.cpp server
+ token="sk-no-key-required", # llama.cpp server requires this placeholder
+)
+
+# Text generation
+response = client.text_generation(
+ "Tell me a story",
+ max_new_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+ details=True,
+)
+print(response.generated_text)
+
+# For chat format
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": "Tell me a story"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+Alternatively, you can use the OpenAI client:
+
+```python
+from openai import OpenAI
+
+# Initialize client pointing to llama.cpp server
+client = OpenAI(
+ base_url="http://localhost:8080/v1",
+ api_key="sk-no-key-required", # llama.cpp server requires this placeholder
+)
+
+# Chat completion
+response = client.chat.completions.create(
+ model="smollm2-1.7b-instruct", # Model identifier can be anything as server only loads one model
+ messages=[
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": "Tell me a story"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+
+
+
+
+vLLM is easy to install and use, with both OpenAI API compatibility and a native Python interface.
+
+First, launch the vLLM OpenAI-compatible server:
+
+```sh
+python -m vllm.entrypoints.openai.api_server \
+ --model HuggingFaceTB/SmolLM2-360M-Instruct \
+ --host 0.0.0.0 \
+ --port 8000
+```
+
+Then interact with it using Hugging Face's InferenceClient:
+
+```python
+from huggingface_hub import InferenceClient
+
+# Initialize client pointing to vLLM endpoint
+client = InferenceClient(
+ model="http://localhost:8000/v1", # URL to the vLLM server
+)
+
+# Text generation
+response = client.text_generation(
+ "Tell me a story",
+ max_new_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+ details=True,
+)
+print(response.generated_text)
+
+# For chat format
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": "Tell me a story"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+Alternatively, you can use the OpenAI client:
+
+```python
+from openai import OpenAI
+
+# Initialize client pointing to vLLM endpoint
+client = OpenAI(
+ base_url="http://localhost:8000/v1",
+ api_key="not-needed", # vLLM doesn't require an API key by default
+)
+
+# Chat completion
+response = client.chat.completions.create(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ messages=[
+ {"role": "system", "content": "You are a helpful assistant."},
+ {"role": "user", "content": "Tell me a story"},
+ ],
+ max_tokens=100,
+ temperature=0.7,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+```
+
+
+
+
+
+### Basic Text Generation
+
+Let's look at examples of text generation with the frameworks:
+
+
+
+
+
+First, deploy TGI with advanced parameters:
+```sh
+docker run --gpus all \
+ --shm-size 1g \
+ -p 8080:80 \
+ -v ~/.cache/huggingface:/data \
+ ghcr.io/huggingface/text-generation-inference:latest \
+ --model-id HuggingFaceTB/SmolLM2-360M-Instruct \
+ --max-total-tokens 4096 \
+ --max-input-length 3072 \
+ --max-batch-total-tokens 8192 \
+ --waiting-served-ratio 1.2
+```
+
+Use the InferenceClient for flexible text generation:
+
+```python
+from huggingface_hub import InferenceClient
+
+client = InferenceClient(model="http://localhost:8080")
+
+# Advanced parameters example
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "You are a creative storyteller."},
+ {"role": "user", "content": "Write a creative story"},
+ ],
+ temperature=0.8,
+ max_tokens=200,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+
+# Raw text generation
+response = client.text_generation(
+ "Write a creative story about space exploration",
+ max_new_tokens=200,
+ temperature=0.8,
+ top_p=0.95,
+ repetition_penalty=1.1,
+ do_sample=True,
+ details=True,
+)
+print(response.generated_text)
+```
+
+Or use the OpenAI client:
+```python
+from openai import OpenAI
+
+client = OpenAI(base_url="http://localhost:8080/v1", api_key="not-needed")
+
+# Advanced parameters example
+response = client.chat.completions.create(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ messages=[
+ {"role": "system", "content": "You are a creative storyteller."},
+ {"role": "user", "content": "Write a creative story"},
+ ],
+ temperature=0.8, # Higher for more creativity
+)
+print(response.choices[0].message.content)
+```
+
+
+
+
+
+For llama.cpp, you can set advanced parameters when launching the server:
+
+```sh
+./server \
+ -m smollm2-1.7b-instruct.Q4_K_M.gguf \
+ --host 0.0.0.0 \
+ --port 8080 \
+ -c 4096 \ # Context size
+ --threads 8 \ # CPU threads to use
+ --batch-size 512 \ # Batch size for prompt evaluation
+ --n-gpu-layers 0 # GPU layers (0 = CPU only)
+```
+
+Use the InferenceClient:
+
+```python
+from huggingface_hub import InferenceClient
+
+client = InferenceClient(model="http://localhost:8080/v1", token="sk-no-key-required")
+
+# Advanced parameters example
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "You are a creative storyteller."},
+ {"role": "user", "content": "Write a creative story"},
+ ],
+ temperature=0.8,
+ max_tokens=200,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+
+# For direct text generation
+response = client.text_generation(
+ "Write a creative story about space exploration",
+ max_new_tokens=200,
+ temperature=0.8,
+ top_p=0.95,
+ repetition_penalty=1.1,
+ details=True,
+)
+print(response.generated_text)
+```
+
+Or use the OpenAI client for generation with control over the sampling parameters:
+
+```python
+from openai import OpenAI
+
+client = OpenAI(base_url="http://localhost:8080/v1", api_key="sk-no-key-required")
+
+# Advanced parameters example
+response = client.chat.completions.create(
+ model="smollm2-1.7b-instruct",
+ messages=[
+ {"role": "system", "content": "You are a creative storyteller."},
+ {"role": "user", "content": "Write a creative story"},
+ ],
+ temperature=0.8, # Higher for more creativity
+ top_p=0.95, # Nucleus sampling probability
+ frequency_penalty=0.5, # Reduce repetition of frequent tokens
+ presence_penalty=0.5, # Reduce repetition by penalizing tokens already present
+ max_tokens=200, # Maximum generation length
+)
+print(response.choices[0].message.content)
+```
+
+You can also use llama.cpp's native library for even more control:
+
+```python
+# Using llama-cpp-python package for direct model access
+from llama_cpp import Llama
+
+# Load the model
+llm = Llama(
+ model_path="smollm2-1.7b-instruct.Q4_K_M.gguf",
+ n_ctx=4096, # Context window size
+ n_threads=8, # CPU threads
+ n_gpu_layers=0, # GPU layers (0 = CPU only)
+)
+
+# Format prompt according to the model's expected format
+prompt = """<|im_start|>system
+You are a creative storyteller.
+<|im_end|>
+<|im_start|>user
+Write a creative story
+<|im_end|>
+<|im_start|>assistant
+"""
+
+# Generate response with precise parameter control
+output = llm(
+ prompt,
+ max_tokens=200,
+ temperature=0.8,
+ top_p=0.95,
+ frequency_penalty=0.5,
+ presence_penalty=0.5,
+ stop=["<|im_end|>"],
+)
+
+print(output["choices"][0]["text"])
+```
+
+
+
+
+
+For advanced usage with vLLM, you can use the InferenceClient:
+
+```python
+from huggingface_hub import InferenceClient
+
+client = InferenceClient(model="http://localhost:8000/v1")
+
+# Advanced parameters example
+response = client.chat_completion(
+ messages=[
+ {"role": "system", "content": "You are a creative storyteller."},
+ {"role": "user", "content": "Write a creative story"},
+ ],
+ temperature=0.8,
+ max_tokens=200,
+ top_p=0.95,
+)
+print(response.choices[0].message.content)
+
+# For direct text generation
+response = client.text_generation(
+ "Write a creative story about space exploration",
+ max_new_tokens=200,
+ temperature=0.8,
+ top_p=0.95,
+ details=True,
+)
+print(response.generated_text)
+```
+
+You can also use the OpenAI client:
+
+```python
+from openai import OpenAI
+
+client = OpenAI(base_url="http://localhost:8000/v1", api_key="not-needed")
+
+# Advanced parameters example
+response = client.chat.completions.create(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ messages=[
+ {"role": "system", "content": "You are a creative storyteller."},
+ {"role": "user", "content": "Write a creative story"},
+ ],
+ temperature=0.8,
+ top_p=0.95,
+ max_tokens=200,
+)
+print(response.choices[0].message.content)
+```
+
+vLLM also provides a native Python interface with fine-grained control:
+
+```python
+from vllm import LLM, SamplingParams
+
+# Initialize the model with advanced parameters
+llm = LLM(
+ model="HuggingFaceTB/SmolLM2-360M-Instruct",
+ gpu_memory_utilization=0.85,
+ max_num_batched_tokens=8192,
+ max_num_seqs=256,
+ block_size=16,
+)
+
+# Configure sampling parameters
+sampling_params = SamplingParams(
+ temperature=0.8, # Higher for more creativity
+ top_p=0.95, # Consider top 95% probability mass
+ max_tokens=100, # Maximum length
+ presence_penalty=1.1, # Reduce repetition
+ frequency_penalty=1.1, # Reduce repetition
+ stop=["\n\n", "###"], # Stop sequences
+)
+
+# Generate text
+prompt = "Write a creative story"
+outputs = llm.generate(prompt, sampling_params)
+print(outputs[0].outputs[0].text)
+
+# For chat-style interactions
+chat_prompt = [
+ {"role": "system", "content": "You are a creative storyteller."},
+ {"role": "user", "content": "Write a creative story"},
+]
+formatted_prompt = llm.get_chat_template()(chat_prompt) # Uses model's chat template
+outputs = llm.generate(formatted_prompt, sampling_params)
+print(outputs[0].outputs[0].text)
+```
+
+
+
+
+
+## Advanced Generation Control
+
+### Token Selection and Sampling
+
+The process of generating text involves selecting the next token at each step. This selection process can be controlled through various parameters:
+
+1. **Raw Logits**: The initial output probabilities for each token
+2. **Temperature**: Controls randomness in selection (higher = more creative)
+3. **Top-p (Nucleus) Sampling**: Filters to top tokens making up X% of probability mass
+4. **Top-k Filtering**: Limits selection to k most likely tokens
+
+Here's how to configure these parameters:
+
+
+
+
+
+```python
+client.generate(
+ "Write a creative story",
+ temperature=0.8, # Higher for more creativity
+ top_p=0.95, # Consider top 95% probability mass
+ top_k=50, # Consider top 50 tokens
+ max_new_tokens=100, # Maximum length
+ repetition_penalty=1.1, # Reduce repetition
+)
+```
+
+
+
+
+
+```python
+# Via OpenAI API compatibility
+response = client.completions.create(
+ model="smollm2-1.7b-instruct", # Model name (can be any string for llama.cpp server)
+ prompt="Write a creative story",
+ temperature=0.8, # Higher for more creativity
+ top_p=0.95, # Consider top 95% probability mass
+ frequency_penalty=1.1, # Reduce repetition
+ presence_penalty=0.1, # Reduce repetition
+ max_tokens=100, # Maximum length
+)
+
+# Via llama-cpp-python direct access
+output = llm(
+ "Write a creative story",
+ temperature=0.8,
+ top_p=0.95,
+ top_k=50,
+ max_tokens=100,
+ repeat_penalty=1.1,
+)
+```
+
+
+
+
+
+```python
+params = SamplingParams(
+ temperature=0.8, # Higher for more creativity
+ top_p=0.95, # Consider top 95% probability mass
+ top_k=50, # Consider top 50 tokens
+ max_tokens=100, # Maximum length
+ presence_penalty=0.1, # Reduce repetition
+)
+llm.generate("Write a creative story", sampling_params=params)
+```
+
+
+
+
+
+### Controlling Repetition
+
+Both frameworks provide ways to prevent repetitive text generation:
+
+
+
+
+
+```python
+client.generate(
+ "Write a varied text",
+ repetition_penalty=1.1, # Penalize repeated tokens
+ no_repeat_ngram_size=3, # Prevent 3-gram repetition
+)
+```
+
+
+
+
+
+```python
+# Via OpenAI API
+response = client.completions.create(
+ model="smollm2-1.7b-instruct",
+ prompt="Write a varied text",
+ frequency_penalty=1.1, # Penalize frequent tokens
+ presence_penalty=0.8, # Penalize tokens already present
+)
+
+# Via direct library
+output = llm(
+ "Write a varied text",
+ repeat_penalty=1.1, # Penalize repeated tokens
+ frequency_penalty=0.5, # Additional frequency penalty
+ presence_penalty=0.5, # Additional presence penalty
+)
+```
+
+
+
+
+
+```python
+params = SamplingParams(
+ presence_penalty=0.1, # Penalize token presence
+ frequency_penalty=0.1, # Penalize token frequency
+)
+```
+
+
+
+
+
+### Length Control and Stop Sequences
+
+You can control generation length and specify when to stop:
+
+
+
+
+
+```python
+client.generate(
+ "Generate a short paragraph",
+ max_new_tokens=100,
+ min_new_tokens=10,
+ stop_sequences=["\n\n", "###"],
+)
+```
+
+
+
+
+
+```python
+# Via OpenAI API
+response = client.completions.create(
+ model="smollm2-1.7b-instruct",
+ prompt="Generate a short paragraph",
+ max_tokens=100,
+ stop=["\n\n", "###"],
+)
+
+# Via direct library
+output = llm("Generate a short paragraph", max_tokens=100, stop=["\n\n", "###"])
+```
+
+
+
+
+
+```python
+params = SamplingParams(
+ max_tokens=100,
+ min_tokens=10,
+ stop=["###", "\n\n"],
+ ignore_eos=False,
+ skip_special_tokens=True,
+)
+```
+
+
+
+
+
+## Memory Management
+
+Both frameworks implement advanced memory management techniques for efficient inference.
+
+
+
+
+
+TGI uses Flash Attention 2 and continuous batching:
+
+```sh
+# Docker deployment with memory optimization
+docker run --gpus all -p 8080:80 \
+ --shm-size 1g \
+ ghcr.io/huggingface/text-generation-inference:latest \
+ --model-id HuggingFaceTB/SmolLM2-1.7B-Instruct \
+ --max-batch-total-tokens 8192 \
+ --max-input-length 4096
+```
+
+
+
+
+
+llama.cpp uses quantization and optimized memory layout:
+
+```sh
+# Server with memory optimizations
+./server \
+ -m smollm2-1.7b-instruct.Q4_K_M.gguf \
+ --host 0.0.0.0 \
+ --port 8080 \
+ -c 2048 \ # Context size
+ --threads 4 \ # CPU threads
+ --n-gpu-layers 32 \ # Use more GPU layers for larger models
+ --mlock \ # Lock memory to prevent swapping
+ --cont-batching # Enable continuous batching
+```
+
+For models too large for your GPU, you can use CPU offloading:
+
+```sh
+./server \
+ -m smollm2-1.7b-instruct.Q4_K_M.gguf \
+ --n-gpu-layers 20 \ # Keep first 20 layers on GPU
+ --threads 8 # Use more CPU threads for CPU layers
+```
+
+
+
+
+
+vLLM uses PagedAttention for optimal memory management:
+
+```python
+from vllm.engine.arg_utils import AsyncEngineArgs
+
+engine_args = AsyncEngineArgs(
+ model="HuggingFaceTB/SmolLM2-1.7B-Instruct",
+ gpu_memory_utilization=0.85,
+ max_num_batched_tokens=8192,
+ block_size=16,
+)
+
+llm = LLM(engine_args=engine_args)
+```
+
+
+
+
+
+## Resources
+
+- [Text Generation Inference Documentation](https://huggingface.co/docs/text-generation-inference)
+- [TGI GitHub Repository](https://github.com/huggingface/text-generation-inference)
+- [vLLM Documentation](https://vllm.readthedocs.io/)
+- [vLLM GitHub Repository](https://github.com/vllm-project/vllm)
+- [PagedAttention Paper](https://arxiv.org/abs/2309.06180)
+- [llama.cpp GitHub Repository](https://github.com/ggerganov/llama.cpp)
+- [llama-cpp-python Repository](https://github.com/abetlen/llama-cpp-python)
diff --git a/chapters/en/chapter2/9.mdx b/chapters/en/chapter2/9.mdx
index 1a375422a..a3a1b871e 100644
--- a/chapters/en/chapter2/9.mdx
+++ b/chapters/en/chapter2/9.mdx
@@ -1,252 +1,252 @@
-
-
-
-
-# End-of-chapter quiz[[end-of-chapter-quiz]]
-
-
-
-### 1. What is the order of the language modeling pipeline?
-
-
-
-### 2. How many dimensions does the tensor output by the base Transformer model have, and what are they?
-
-
-
-### 3. Which of the following is an example of subword tokenization?
-
-
-
-### 4. What is a model head?
-
-
-
-### 5. What is an AutoModel?
-
-AutoTrain product?"
- },
- {
- text: "An object that returns the correct architecture based on the checkpoint",
- explain: "Exactly: the AutoModel only needs to know the checkpoint from which to initialize to return the correct architecture.",
- correct: true
- },
- {
- text: "A model that automatically detects the language used for its inputs to load the correct weights",
- explain: "While some checkpoints and models are capable of handling multiple languages, there are no built-in tools for automatic checkpoint selection according to language. You should head over to the Model Hub to find the best checkpoint for your task!"
- }
- ]}
-/>
-
-### 6. What are the techniques to be aware of when batching sequences of different lengths together?
-
-
-
-### 7. What is the point of applying a SoftMax function to the logits output by a sequence classification model?
-
-
-
-### 8. What method is most of the tokenizer API centered around?
-
-encode, as it can encode text into IDs and IDs into predictions",
- explain: "Wrong! While the encode method does exist on tokenizers, it does not exist on models."
- },
- {
- text: "Calling the tokenizer object directly.",
- explain: "Exactly! The __call__ method of the tokenizer is a very powerful method which can handle pretty much anything. It is also the method used to retrieve predictions from a model.",
- correct: true
- },
- {
- text: "pad",
- explain: "Wrong! Padding is very useful, but it's just one part of the tokenizer API."
- },
- {
- text: "tokenize",
- explain: "The tokenize method is arguably one of the most useful methods, but it isn't the core of the tokenizer API."
- }
- ]}
-/>
-
-### 9. What does the `result` variable contain in this code sample?
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-result = tokenizer.tokenize("Hello!")
-```
-
-__call__ or convert_tokens_to_ids method is for!"
- },
- {
- text: "A string containing all of the tokens",
- explain: "This would be suboptimal, as the goal is to split the string into multiple tokens."
- }
- ]}
-/>
-
-### 10. Is there something wrong with the following code?
-
-```py
-from transformers import AutoTokenizer, AutoModel
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-model = AutoModel.from_pretrained("gpt2")
-
-encoded = tokenizer("Hey!", return_tensors="pt")
-result = model(**encoded)
-```
-
-
+
+
+
+
+# End-of-chapter quiz[[end-of-chapter-quiz]]
+
+
+
+### 1. What is the order of the language modeling pipeline?
+
+
+
+### 2. How many dimensions does the tensor output by the base Transformer model have, and what are they?
+
+
+
+### 3. Which of the following is an example of subword tokenization?
+
+
+
+### 4. What is a model head?
+
+
+
+### 5. What is an AutoModel?
+
+AutoTrain product?"
+ },
+ {
+ text: "An object that returns the correct architecture based on the checkpoint",
+ explain: "Exactly: the AutoModel only needs to know the checkpoint from which to initialize to return the correct architecture.",
+ correct: true
+ },
+ {
+ text: "A model that automatically detects the language used for its inputs to load the correct weights",
+ explain: "While some checkpoints and models are capable of handling multiple languages, there are no built-in tools for automatic checkpoint selection according to language. You should head over to the Model Hub to find the best checkpoint for your task!"
+ }
+ ]}
+/>
+
+### 6. What are the techniques to be aware of when batching sequences of different lengths together?
+
+
+
+### 7. What is the point of applying a SoftMax function to the logits output by a sequence classification model?
+
+
+
+### 8. What method is most of the tokenizer API centered around?
+
+encode, as it can encode text into IDs and IDs into predictions",
+ explain: "Wrong! While the encode method does exist on tokenizers, it does not exist on models."
+ },
+ {
+ text: "Calling the tokenizer object directly.",
+ explain: "Exactly! The __call__ method of the tokenizer is a very powerful method which can handle pretty much anything. It is also the method used to retrieve predictions from a model.",
+ correct: true
+ },
+ {
+ text: "pad",
+ explain: "Wrong! Padding is very useful, but it's just one part of the tokenizer API."
+ },
+ {
+ text: "tokenize",
+ explain: "The tokenize method is arguably one of the most useful methods, but it isn't the core of the tokenizer API."
+ }
+ ]}
+/>
+
+### 9. What does the `result` variable contain in this code sample?
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+result = tokenizer.tokenize("Hello!")
+```
+
+__call__ or convert_tokens_to_ids method is for!"
+ },
+ {
+ text: "A string containing all of the tokens",
+ explain: "This would be suboptimal, as the goal is to split the string into multiple tokens."
+ }
+ ]}
+/>
+
+### 10. Is there something wrong with the following code?
+
+```py
+from transformers import AutoTokenizer, AutoModel
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+model = AutoModel.from_pretrained("gpt2")
+
+encoded = tokenizer("Hey!", return_tensors="pt")
+result = model(**encoded)
+```
+
+
diff --git a/chapters/en/chapter3/1.mdx b/chapters/en/chapter3/1.mdx
index 2776eb1c2..c499eb385 100644
--- a/chapters/en/chapter3/1.mdx
+++ b/chapters/en/chapter3/1.mdx
@@ -1,35 +1,35 @@
-
-
-# Introduction[[introduction]]
-
-
-
-In [Chapter 2](/course/chapter2) we explored how to use tokenizers and pretrained models to make predictions. But what if you want to fine-tune a pretrained model to solve a specific task? That's the topic of this chapter! You will learn:
-
-* How to prepare a large dataset from the Hub using the latest 🤗 Datasets features
-* How to use the high-level `Trainer` API to fine-tune a model with modern best practices
-* How to implement a custom training loop with optimization techniques
-* How to leverage the 🤗 Accelerate library to easily run distributed training on any setup
-* How to apply current fine-tuning best practices for maximum performance
-
-> [!TIP]
-> 📚 **Essential Resources**: Before starting, you might want to review the [🤗 Datasets documentation](https://huggingface.co/docs/datasets/) for data processing.
-
-This chapter will also serve as an introduction to some Hugging Face libraries beyond the 🤗 Transformers library! We'll see how libraries like 🤗 Datasets, 🤗 Tokenizers, 🤗 Accelerate, and 🤗 Evaluate can help you train models more efficiently and effectively.
-
-Each of the main sections in this chapter will teach you something different:
-- **Section 2**: Learn modern data preprocessing techniques and efficient dataset handling
-- **Section 3**: Master the powerful Trainer API with all its latest features
-- **Section 4**: Implement training loops from scratch and understand distributed training with Accelerate
-
-By the end of this chapter, you'll be able to fine-tune models on your own datasets using both high-level APIs and custom training loops, applying the latest best practices in the field.
-
-> [!TIP]
-> 🎯 **What You'll Build**: By the end of this chapter, you'll have fine-tuned a BERT model for text classification and understand how to adapt the techniques to your own datasets and tasks.
-
-This chapter focuses exclusively on **PyTorch**, as it has become the standard framework for modern deep learning research and production. We'll use the latest APIs and best practices from the Hugging Face ecosystem.
-
+
+
+# Introduction[[introduction]]
+
+
+
+In [Chapter 2](/course/chapter2) we explored how to use tokenizers and pretrained models to make predictions. But what if you want to fine-tune a pretrained model to solve a specific task? That's the topic of this chapter! You will learn:
+
+* How to prepare a large dataset from the Hub using the latest 🤗 Datasets features
+* How to use the high-level `Trainer` API to fine-tune a model with modern best practices
+* How to implement a custom training loop with optimization techniques
+* How to leverage the 🤗 Accelerate library to easily run distributed training on any setup
+* How to apply current fine-tuning best practices for maximum performance
+
+> [!TIP]
+> 📚 **Essential Resources**: Before starting, you might want to review the [🤗 Datasets documentation](https://huggingface.co/docs/datasets/) for data processing.
+
+This chapter will also serve as an introduction to some Hugging Face libraries beyond the 🤗 Transformers library! We'll see how libraries like 🤗 Datasets, 🤗 Tokenizers, 🤗 Accelerate, and 🤗 Evaluate can help you train models more efficiently and effectively.
+
+Each of the main sections in this chapter will teach you something different:
+- **Section 2**: Learn modern data preprocessing techniques and efficient dataset handling
+- **Section 3**: Master the powerful Trainer API with all its latest features
+- **Section 4**: Implement training loops from scratch and understand distributed training with Accelerate
+
+By the end of this chapter, you'll be able to fine-tune models on your own datasets using both high-level APIs and custom training loops, applying the latest best practices in the field.
+
+> [!TIP]
+> 🎯 **What You'll Build**: By the end of this chapter, you'll have fine-tuned a BERT model for text classification and understand how to adapt the techniques to your own datasets and tasks.
+
+This chapter focuses exclusively on **PyTorch**, as it has become the standard framework for modern deep learning research and production. We'll use the latest APIs and best practices from the Hugging Face ecosystem.
+
To upload your trained models to the Hugging Face Hub, you will need a Hugging Face account: [create an account](https://huggingface.co/join)
\ No newline at end of file
diff --git a/chapters/en/chapter3/2.mdx b/chapters/en/chapter3/2.mdx
index 232441b84..7f095a8d2 100644
--- a/chapters/en/chapter3/2.mdx
+++ b/chapters/en/chapter3/2.mdx
@@ -1,419 +1,419 @@
-# Processing the data[[processing-the-data]]
-
-
-
-Continuing with the example from the [previous chapter](/course/chapter2), here is how we would train a sequence classifier on one batch:
-
-```python
-import torch
-from torch.optim import AdamW
-from transformers import AutoTokenizer, AutoModelForSequenceClassification
-
-# Same as before
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
-sequences = [
- "I've been waiting for a HuggingFace course my whole life.",
- "This course is amazing!",
-]
-batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
-
-# This is new
-batch["labels"] = torch.tensor([1, 1])
-
-optimizer = AdamW(model.parameters())
-loss = model(**batch).loss
-loss.backward()
-optimizer.step()
-```
-
-Of course, just training the model on two sentences is not going to yield very good results. To get better results, you will need to prepare a bigger dataset.
-
-In this section we will use as an example the MRPC (Microsoft Research Paraphrase Corpus) dataset, introduced in a [paper](https://www.aclweb.org/anthology/I05-5002.pdf) by William B. Dolan and Chris Brockett. The dataset consists of 5,801 pairs of sentences, with a label indicating if they are paraphrases or not (i.e., if both sentences mean the same thing). We've selected it for this chapter because it's a small dataset, so it's easy to experiment with training on it.
-
-### Loading a dataset from the Hub[[loading-a-dataset-from-the-hub]]
-
-
-
-The Hub doesn't just contain models; it also has multiple datasets in lots of different languages. You can browse the datasets [here](https://huggingface.co/datasets), and we recommend you try to load and process a new dataset once you have gone through this section (see the general documentation [here](https://huggingface.co/docs/datasets/loading)). But for now, let's focus on the MRPC dataset! This is one of the 10 datasets composing the [GLUE benchmark](https://gluebenchmark.com/), which is an academic benchmark that is used to measure the performance of ML models across 10 different text classification tasks.
-
-The 🤗 Datasets library provides a very simple command to download and cache a dataset on the Hub. We can download the MRPC dataset like this:
-
-> [!TIP]
-> 💡 **Additional Resources**: For more dataset loading techniques and examples, check out the [🤗 Datasets documentation](https://huggingface.co/docs/datasets/).
-
-```py
-from datasets import load_dataset
-
-raw_datasets = load_dataset("glue", "mrpc")
-raw_datasets
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['sentence1', 'sentence2', 'label', 'idx'],
- num_rows: 3668
- })
- validation: Dataset({
- features: ['sentence1', 'sentence2', 'label', 'idx'],
- num_rows: 408
- })
- test: Dataset({
- features: ['sentence1', 'sentence2', 'label', 'idx'],
- num_rows: 1725
- })
-})
-```
-
-As you can see, we get a `DatasetDict` object which contains the training set, the validation set, and the test set. Each of those contains several columns (`sentence1`, `sentence2`, `label`, and `idx`) and a variable number of rows, which are the number of elements in each set (so, there are 3,668 pairs of sentences in the training set, 408 in the validation set, and 1,725 in the test set).
-
-> [!TIP]
-> This command downloads and caches the dataset, by default in *~/.cache/huggingface/datasets*. Recall from Chapter 2 that you can customize your cache folder by setting the `HF_HOME` environment variable.
-
-We can access each pair of sentences in our `raw_datasets` object by indexing, like with a dictionary:
-
-```py
-raw_train_dataset = raw_datasets["train"]
-raw_train_dataset[0]
-```
-
-```python out
-{'idx': 0,
- 'label': 1,
- 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
- 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
-```
-
-We can see the labels are already integers, so we won't have to do any preprocessing there. To know which integer corresponds to which label, we can inspect the `features` of our `raw_train_dataset`. This will tell us the type of each column:
-
-```py
-raw_train_dataset.features
-```
-
-```python out
-{'sentence1': Value(dtype='string', id=None),
- 'sentence2': Value(dtype='string', id=None),
- 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
- 'idx': Value(dtype='int32', id=None)}
-```
-
-Behind the scenes, `label` is of type `ClassLabel`, and the mapping of integers to label name is stored in the *names* folder. `0` corresponds to `not_equivalent`, and `1` corresponds to `equivalent`.
-
-> [!TIP]
-> ✏️ **Try it out!** Look at element 15 of the training set and element 87 of the validation set. What are their labels?
-
-### Preprocessing a dataset[[preprocessing-a-dataset]]
-
-
-
-To preprocess the dataset, we need to convert the text to numbers the model can make sense of. As you saw in the [previous chapter](/course/chapter2), this is done with a tokenizer. We can feed the tokenizer one sentence or a list of sentences, so we can directly tokenize all the first sentences and all the second sentences of each pair like this:
-
-```py
-from transformers import AutoTokenizer
-
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
-tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
-```
-
-> [!TIP]
-> 💡 **Deep Dive**: For more advanced tokenization techniques and understanding how different tokenizers work, explore the [🤗 Tokenizers documentation](https://huggingface.co/docs/transformers/main/en/tokenizer_summary) and the [tokenization guide in the cookbook](https://huggingface.co/learn/cookbook/en/advanced_rag#tokenization-strategies).
-
-However, we can't just pass two sequences to the model and get a prediction of whether the two sentences are paraphrases or not. We need to handle the two sequences as a pair, and apply the appropriate preprocessing. Fortunately, the tokenizer can also take a pair of sequences and prepare it the way our BERT model expects:
-
-```py
-inputs = tokenizer("This is the first sentence.", "This is the second one.")
-inputs
-```
-
-```python out
-{
- 'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
- 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
- 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
-}
-```
-
-We discussed the `input_ids` and `attention_mask` keys in [Chapter 2](/course/chapter2), but we put off talking about `token_type_ids`. In this example, this is what tells the model which part of the input is the first sentence and which is the second sentence.
-
-> [!TIP]
-> ✏️ **Try it out!** Take element 15 of the training set and tokenize the two sentences separately and as a pair. What's the difference between the two results?
-
-If we decode the IDs inside `input_ids` back to words:
-
-```py
-tokenizer.convert_ids_to_tokens(inputs["input_ids"])
-```
-
-we will get:
-
-```python out
-['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
-```
-
-So we see the model expects the inputs to be of the form `[CLS] sentence1 [SEP] sentence2 [SEP]` when there are two sentences. Aligning this with the `token_type_ids` gives us:
-
-```python out
-['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
-[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
-```
-
-As you can see, the parts of the input corresponding to `[CLS] sentence1 [SEP]` all have a token type ID of `0`, while the other parts, corresponding to `sentence2 [SEP]`, all have a token type ID of `1`.
-
-Note that if you select a different checkpoint, you won't necessarily have the `token_type_ids` in your tokenized inputs (for instance, they're not returned if you use a DistilBERT model). They are only returned when the model will know what to do with them, because it has seen them during its pretraining.
-
-Here, BERT is pretrained with token type IDs, and on top of the masked language modeling objective we talked about in [Chapter 1](/course/chapter1), it has an additional objective called _next sentence prediction_. The goal with this task is to model the relationship between pairs of sentences.
-
-With next sentence prediction, the model is provided pairs of sentences (with randomly masked tokens) and asked to predict whether the second sentence follows the first. To make the task non-trivial, half of the time the sentences follow each other in the original document they were extracted from, and the other half of the time the two sentences come from two different documents.
-
-In general, you don't need to worry about whether or not there are `token_type_ids` in your tokenized inputs: as long as you use the same checkpoint for the tokenizer and the model, everything will be fine as the tokenizer knows what to provide to its model.
-
-Now that we have seen how our tokenizer can deal with one pair of sentences, we can use it to tokenize our whole dataset: like in the [previous chapter](/course/chapter2), we can feed the tokenizer a list of pairs of sentences by giving it the list of first sentences, then the list of second sentences. This is also compatible with the padding and truncation options we saw in [Chapter 2](/course/chapter2). So, one way to preprocess the training dataset is:
-
-```py
-tokenized_dataset = tokenizer(
- raw_datasets["train"]["sentence1"],
- raw_datasets["train"]["sentence2"],
- padding=True,
- truncation=True,
-)
-```
-
-This works well, but it has the disadvantage of returning a dictionary (with our keys, `input_ids`, `attention_mask`, and `token_type_ids`, and values that are lists of lists). It will also only work if you have enough RAM to store your whole dataset during the tokenization (whereas the datasets from the 🤗 Datasets library are [Apache Arrow](https://arrow.apache.org/) files stored on the disk, so you only keep the samples you ask for loaded in memory).
-
-To keep the data as a dataset, we will use the [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map) method. This also allows us some extra flexibility, if we need more preprocessing done than just tokenization. The `map()` method works by applying a function on each element of the dataset, so let's define a function that tokenizes our inputs:
-
-```py
-def tokenize_function(example):
- return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
-```
-
-This function takes a dictionary (like the items of our dataset) and returns a new dictionary with the keys `input_ids`, `attention_mask`, and `token_type_ids`. Note that it also works if the `example` dictionary contains several samples (each key as a list of sentences) since the `tokenizer` works on lists of pairs of sentences, as seen before. This will allow us to use the option `batched=True` in our call to `map()`, which will greatly speed up the tokenization. The `tokenizer` is backed by a tokenizer written in Rust from the [🤗 Tokenizers](https://github.com/huggingface/tokenizers) library. This tokenizer can be very fast, but only if we give it lots of inputs at once.
-
-Note that we've left the `padding` argument out in our tokenization function for now. This is because padding all the samples to the maximum length is not efficient: it's better to pad the samples when we're building a batch, as then we only need to pad to the maximum length in that batch, and not the maximum length in the entire dataset. This can save a lot of time and processing power when the inputs have very variable lengths!
-
-> [!TIP]
-> 📚 **Performance Tips**: Learn more about efficient data processing techniques in the [🤗 Datasets performance guide](https://huggingface.co/docs/datasets/about_arrow).
-
-Here is how we apply the tokenization function on all our datasets at once. We're using `batched=True` in our call to `map` so the function is applied to multiple elements of our dataset at once, and not on each element separately. This allows for faster preprocessing.
-
-```py
-tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
-tokenized_datasets
-```
-
-The way the 🤗 Datasets library applies this processing is by adding new fields to the datasets, one for each key in the dictionary returned by the preprocessing function:
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
- num_rows: 3668
- })
- validation: Dataset({
- features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
- num_rows: 408
- })
- test: Dataset({
- features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
- num_rows: 1725
- })
-})
-```
-
-You can even use multiprocessing when applying your preprocessing function with `map()` by passing along a `num_proc` argument. We didn't do this here because the 🤗 Tokenizers library already uses multiple threads to tokenize our samples faster, but if you are not using a fast tokenizer backed by this library, this could speed up your preprocessing.
-
-Our `tokenize_function` returns a dictionary with the keys `input_ids`, `attention_mask`, and `token_type_ids`, so those three fields are added to all splits of our dataset. Note that we could also have changed existing fields if our preprocessing function returned a new value for an existing key in the dataset to which we applied `map()`.
-
-The last thing we will need to do is pad all the examples to the length of the longest element when we batch elements together — a technique we refer to as *dynamic padding*.
-
-##### Dynamic padding[[dynamic-padding]]
-
-
-
-The function that is responsible for putting together samples inside a batch is called a *collate function*. It's an argument you can pass when you build a `DataLoader`, the default being a function that will just convert your samples to PyTorch tensors and concatenate them (recursively if your elements are lists, tuples, or dictionaries). This won't be possible in our case since the inputs we have won't all be of the same size. We have deliberately postponed the padding, to only apply it as necessary on each batch and avoid having over-long inputs with a lot of padding. This will speed up training by quite a bit, but note that if you're training on a TPU it can cause problems — TPUs prefer fixed shapes, even when that requires extra padding.
-
-> [!TIP]
-> 🚀 **Optimization Guide**: For more details on optimizing training performance, including padding strategies and TPU considerations, see the [🤗 Transformers performance documentation](https://huggingface.co/docs/transformers/main/en/performance).
-
-To do this in practice, we have to define a collate function that will apply the correct amount of padding to the items of the dataset we want to batch together. Fortunately, the 🤗 Transformers library provides us with such a function via `DataCollatorWithPadding`. It takes a tokenizer when you instantiate it (to know which padding token to use, and whether the model expects padding to be on the left or on the right of the inputs) and will do everything you need:
-
-```py
-from transformers import DataCollatorWithPadding
-
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
-```
-
-To test this new toy, let's grab a few samples from our training set that we would like to batch together. Here, we remove the columns `idx`, `sentence1`, and `sentence2` as they won't be needed and contain strings (and we can't create tensors with strings) and have a look at the lengths of each entry in the batch:
-
-```py
-samples = tokenized_datasets["train"][:8]
-samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
-[len(x) for x in samples["input_ids"]]
-```
-
-```python out
-[50, 59, 47, 67, 59, 50, 62, 32]
-```
-
-No surprise, we get samples of varying length, from 32 to 67. Dynamic padding means the samples in this batch should all be padded to a length of 67, the maximum length inside the batch. Without dynamic padding, all of the samples would have to be padded to the maximum length in the whole dataset, or the maximum length the model can accept. Let's double-check that our `data_collator` is dynamically padding the batch properly:
-
-```py
-batch = data_collator(samples)
-{k: v.shape for k, v in batch.items()}
-```
-
-```python out
-{'attention_mask': torch.Size([8, 67]),
- 'input_ids': torch.Size([8, 67]),
- 'token_type_ids': torch.Size([8, 67]),
- 'labels': torch.Size([8])}
-```
-
-Looking good! Now that we've gone from raw text to batches our model can deal with, we're ready to fine-tune it!
-
-> [!TIP]
-> ✏️ **Try it out!** Replicate the preprocessing on the GLUE SST-2 dataset. It's a little bit different since it's composed of single sentences instead of pairs, but the rest of what we did should look the same. For a harder challenge, try to write a preprocessing function that works on any of the GLUE tasks.
->
-> 📖 **Additional Practice**: Check out these hands-on examples from the [🤗 Transformers examples](https://huggingface.co/docs/transformers/main/en/notebooks).
-
-Perfect! Now that we have preprocessed our data with the latest best practices from the 🤗 Datasets library, we're ready to move on to training our model using the modern Trainer API. The next section will show you how to fine-tune your model effectively using the latest features and optimizations available in the Hugging Face ecosystem.
-
-## Section Quiz[[section-quiz]]
-
-Test your understanding of data processing concepts:
-
-### 1. What is the main advantage of using `Dataset.map()` with `batched=True`?
-
-
-
-### 2. Why do we use dynamic padding instead of padding all sequences to the maximum length in the dataset?
-
-
-
-### 3. What does the `token_type_ids` field represent in BERT tokenization?
-
-
-
-### 4. When loading a dataset with `load_dataset('glue', 'mrpc')`, what does the second argument specify?
-
-
-
-### 5. What is the purpose of removing columns like 'sentence1' and 'sentence2' before training?
-
-
-
-> [!TIP]
-> 💡 **Key Takeaways:**
-> - Use `batched=True` with `Dataset.map()` for significantly faster preprocessing
-> - Dynamic padding with `DataCollatorWithPadding` is more efficient than fixed-length padding
-> - Always preprocess your data to match what your model expects (numerical tensors, correct column names)
-> - The 🤗 Datasets library provides powerful tools for efficient data processing at scale
+# Processing the data[[processing-the-data]]
+
+
+
+Continuing with the example from the [previous chapter](/course/chapter2), here is how we would train a sequence classifier on one batch:
+
+```python
+import torch
+from torch.optim import AdamW
+from transformers import AutoTokenizer, AutoModelForSequenceClassification
+
+# Same as before
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint)
+sequences = [
+ "I've been waiting for a HuggingFace course my whole life.",
+ "This course is amazing!",
+]
+batch = tokenizer(sequences, padding=True, truncation=True, return_tensors="pt")
+
+# This is new
+batch["labels"] = torch.tensor([1, 1])
+
+optimizer = AdamW(model.parameters())
+loss = model(**batch).loss
+loss.backward()
+optimizer.step()
+```
+
+Of course, just training the model on two sentences is not going to yield very good results. To get better results, you will need to prepare a bigger dataset.
+
+In this section we will use as an example the MRPC (Microsoft Research Paraphrase Corpus) dataset, introduced in a [paper](https://www.aclweb.org/anthology/I05-5002.pdf) by William B. Dolan and Chris Brockett. The dataset consists of 5,801 pairs of sentences, with a label indicating if they are paraphrases or not (i.e., if both sentences mean the same thing). We've selected it for this chapter because it's a small dataset, so it's easy to experiment with training on it.
+
+### Loading a dataset from the Hub[[loading-a-dataset-from-the-hub]]
+
+
+
+The Hub doesn't just contain models; it also has multiple datasets in lots of different languages. You can browse the datasets [here](https://huggingface.co/datasets), and we recommend you try to load and process a new dataset once you have gone through this section (see the general documentation [here](https://huggingface.co/docs/datasets/loading)). But for now, let's focus on the MRPC dataset! This is one of the 10 datasets composing the [GLUE benchmark](https://gluebenchmark.com/), which is an academic benchmark that is used to measure the performance of ML models across 10 different text classification tasks.
+
+The 🤗 Datasets library provides a very simple command to download and cache a dataset on the Hub. We can download the MRPC dataset like this:
+
+> [!TIP]
+> 💡 **Additional Resources**: For more dataset loading techniques and examples, check out the [🤗 Datasets documentation](https://huggingface.co/docs/datasets/).
+
+```py
+from datasets import load_dataset
+
+raw_datasets = load_dataset("glue", "mrpc")
+raw_datasets
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 3668
+ })
+ validation: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 408
+ })
+ test: Dataset({
+ features: ['sentence1', 'sentence2', 'label', 'idx'],
+ num_rows: 1725
+ })
+})
+```
+
+As you can see, we get a `DatasetDict` object which contains the training set, the validation set, and the test set. Each of those contains several columns (`sentence1`, `sentence2`, `label`, and `idx`) and a variable number of rows, which are the number of elements in each set (so, there are 3,668 pairs of sentences in the training set, 408 in the validation set, and 1,725 in the test set).
+
+> [!TIP]
+> This command downloads and caches the dataset, by default in *~/.cache/huggingface/datasets*. Recall from Chapter 2 that you can customize your cache folder by setting the `HF_HOME` environment variable.
+
+We can access each pair of sentences in our `raw_datasets` object by indexing, like with a dictionary:
+
+```py
+raw_train_dataset = raw_datasets["train"]
+raw_train_dataset[0]
+```
+
+```python out
+{'idx': 0,
+ 'label': 1,
+ 'sentence1': 'Amrozi accused his brother , whom he called " the witness " , of deliberately distorting his evidence .',
+ 'sentence2': 'Referring to him as only " the witness " , Amrozi accused his brother of deliberately distorting his evidence .'}
+```
+
+We can see the labels are already integers, so we won't have to do any preprocessing there. To know which integer corresponds to which label, we can inspect the `features` of our `raw_train_dataset`. This will tell us the type of each column:
+
+```py
+raw_train_dataset.features
+```
+
+```python out
+{'sentence1': Value(dtype='string', id=None),
+ 'sentence2': Value(dtype='string', id=None),
+ 'label': ClassLabel(num_classes=2, names=['not_equivalent', 'equivalent'], names_file=None, id=None),
+ 'idx': Value(dtype='int32', id=None)}
+```
+
+Behind the scenes, `label` is of type `ClassLabel`, and the mapping of integers to label name is stored in the *names* folder. `0` corresponds to `not_equivalent`, and `1` corresponds to `equivalent`.
+
+> [!TIP]
+> ✏️ **Try it out!** Look at element 15 of the training set and element 87 of the validation set. What are their labels?
+
+### Preprocessing a dataset[[preprocessing-a-dataset]]
+
+
+
+To preprocess the dataset, we need to convert the text to numbers the model can make sense of. As you saw in the [previous chapter](/course/chapter2), this is done with a tokenizer. We can feed the tokenizer one sentence or a list of sentences, so we can directly tokenize all the first sentences and all the second sentences of each pair like this:
+
+```py
+from transformers import AutoTokenizer
+
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+tokenized_sentences_1 = tokenizer(raw_datasets["train"]["sentence1"])
+tokenized_sentences_2 = tokenizer(raw_datasets["train"]["sentence2"])
+```
+
+> [!TIP]
+> 💡 **Deep Dive**: For more advanced tokenization techniques and understanding how different tokenizers work, explore the [🤗 Tokenizers documentation](https://huggingface.co/docs/transformers/main/en/tokenizer_summary) and the [tokenization guide in the cookbook](https://huggingface.co/learn/cookbook/en/advanced_rag#tokenization-strategies).
+
+However, we can't just pass two sequences to the model and get a prediction of whether the two sentences are paraphrases or not. We need to handle the two sequences as a pair, and apply the appropriate preprocessing. Fortunately, the tokenizer can also take a pair of sequences and prepare it the way our BERT model expects:
+
+```py
+inputs = tokenizer("This is the first sentence.", "This is the second one.")
+inputs
+```
+
+```python out
+{
+ 'input_ids': [101, 2023, 2003, 1996, 2034, 6251, 1012, 102, 2023, 2003, 1996, 2117, 2028, 1012, 102],
+ 'token_type_ids': [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1],
+ 'attention_mask': [1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
+}
+```
+
+We discussed the `input_ids` and `attention_mask` keys in [Chapter 2](/course/chapter2), but we put off talking about `token_type_ids`. In this example, this is what tells the model which part of the input is the first sentence and which is the second sentence.
+
+> [!TIP]
+> ✏️ **Try it out!** Take element 15 of the training set and tokenize the two sentences separately and as a pair. What's the difference between the two results?
+
+If we decode the IDs inside `input_ids` back to words:
+
+```py
+tokenizer.convert_ids_to_tokens(inputs["input_ids"])
+```
+
+we will get:
+
+```python out
+['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
+```
+
+So we see the model expects the inputs to be of the form `[CLS] sentence1 [SEP] sentence2 [SEP]` when there are two sentences. Aligning this with the `token_type_ids` gives us:
+
+```python out
+['[CLS]', 'this', 'is', 'the', 'first', 'sentence', '.', '[SEP]', 'this', 'is', 'the', 'second', 'one', '.', '[SEP]']
+[ 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
+```
+
+As you can see, the parts of the input corresponding to `[CLS] sentence1 [SEP]` all have a token type ID of `0`, while the other parts, corresponding to `sentence2 [SEP]`, all have a token type ID of `1`.
+
+Note that if you select a different checkpoint, you won't necessarily have the `token_type_ids` in your tokenized inputs (for instance, they're not returned if you use a DistilBERT model). They are only returned when the model will know what to do with them, because it has seen them during its pretraining.
+
+Here, BERT is pretrained with token type IDs, and on top of the masked language modeling objective we talked about in [Chapter 1](/course/chapter1), it has an additional objective called _next sentence prediction_. The goal with this task is to model the relationship between pairs of sentences.
+
+With next sentence prediction, the model is provided pairs of sentences (with randomly masked tokens) and asked to predict whether the second sentence follows the first. To make the task non-trivial, half of the time the sentences follow each other in the original document they were extracted from, and the other half of the time the two sentences come from two different documents.
+
+In general, you don't need to worry about whether or not there are `token_type_ids` in your tokenized inputs: as long as you use the same checkpoint for the tokenizer and the model, everything will be fine as the tokenizer knows what to provide to its model.
+
+Now that we have seen how our tokenizer can deal with one pair of sentences, we can use it to tokenize our whole dataset: like in the [previous chapter](/course/chapter2), we can feed the tokenizer a list of pairs of sentences by giving it the list of first sentences, then the list of second sentences. This is also compatible with the padding and truncation options we saw in [Chapter 2](/course/chapter2). So, one way to preprocess the training dataset is:
+
+```py
+tokenized_dataset = tokenizer(
+ raw_datasets["train"]["sentence1"],
+ raw_datasets["train"]["sentence2"],
+ padding=True,
+ truncation=True,
+)
+```
+
+This works well, but it has the disadvantage of returning a dictionary (with our keys, `input_ids`, `attention_mask`, and `token_type_ids`, and values that are lists of lists). It will also only work if you have enough RAM to store your whole dataset during the tokenization (whereas the datasets from the 🤗 Datasets library are [Apache Arrow](https://arrow.apache.org/) files stored on the disk, so you only keep the samples you ask for loaded in memory).
+
+To keep the data as a dataset, we will use the [`Dataset.map()`](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map) method. This also allows us some extra flexibility, if we need more preprocessing done than just tokenization. The `map()` method works by applying a function on each element of the dataset, so let's define a function that tokenizes our inputs:
+
+```py
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+```
+
+This function takes a dictionary (like the items of our dataset) and returns a new dictionary with the keys `input_ids`, `attention_mask`, and `token_type_ids`. Note that it also works if the `example` dictionary contains several samples (each key as a list of sentences) since the `tokenizer` works on lists of pairs of sentences, as seen before. This will allow us to use the option `batched=True` in our call to `map()`, which will greatly speed up the tokenization. The `tokenizer` is backed by a tokenizer written in Rust from the [🤗 Tokenizers](https://github.com/huggingface/tokenizers) library. This tokenizer can be very fast, but only if we give it lots of inputs at once.
+
+Note that we've left the `padding` argument out in our tokenization function for now. This is because padding all the samples to the maximum length is not efficient: it's better to pad the samples when we're building a batch, as then we only need to pad to the maximum length in that batch, and not the maximum length in the entire dataset. This can save a lot of time and processing power when the inputs have very variable lengths!
+
+> [!TIP]
+> 📚 **Performance Tips**: Learn more about efficient data processing techniques in the [🤗 Datasets performance guide](https://huggingface.co/docs/datasets/about_arrow).
+
+Here is how we apply the tokenization function on all our datasets at once. We're using `batched=True` in our call to `map` so the function is applied to multiple elements of our dataset at once, and not on each element separately. This allows for faster preprocessing.
+
+```py
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+tokenized_datasets
+```
+
+The way the 🤗 Datasets library applies this processing is by adding new fields to the datasets, one for each key in the dictionary returned by the preprocessing function:
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 3668
+ })
+ validation: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 408
+ })
+ test: Dataset({
+ features: ['attention_mask', 'idx', 'input_ids', 'label', 'sentence1', 'sentence2', 'token_type_ids'],
+ num_rows: 1725
+ })
+})
+```
+
+You can even use multiprocessing when applying your preprocessing function with `map()` by passing along a `num_proc` argument. We didn't do this here because the 🤗 Tokenizers library already uses multiple threads to tokenize our samples faster, but if you are not using a fast tokenizer backed by this library, this could speed up your preprocessing.
+
+Our `tokenize_function` returns a dictionary with the keys `input_ids`, `attention_mask`, and `token_type_ids`, so those three fields are added to all splits of our dataset. Note that we could also have changed existing fields if our preprocessing function returned a new value for an existing key in the dataset to which we applied `map()`.
+
+The last thing we will need to do is pad all the examples to the length of the longest element when we batch elements together — a technique we refer to as *dynamic padding*.
+
+##### Dynamic padding[[dynamic-padding]]
+
+
+
+The function that is responsible for putting together samples inside a batch is called a *collate function*. It's an argument you can pass when you build a `DataLoader`, the default being a function that will just convert your samples to PyTorch tensors and concatenate them (recursively if your elements are lists, tuples, or dictionaries). This won't be possible in our case since the inputs we have won't all be of the same size. We have deliberately postponed the padding, to only apply it as necessary on each batch and avoid having over-long inputs with a lot of padding. This will speed up training by quite a bit, but note that if you're training on a TPU it can cause problems — TPUs prefer fixed shapes, even when that requires extra padding.
+
+> [!TIP]
+> 🚀 **Optimization Guide**: For more details on optimizing training performance, including padding strategies and TPU considerations, see the [🤗 Transformers performance documentation](https://huggingface.co/docs/transformers/main/en/performance).
+
+To do this in practice, we have to define a collate function that will apply the correct amount of padding to the items of the dataset we want to batch together. Fortunately, the 🤗 Transformers library provides us with such a function via `DataCollatorWithPadding`. It takes a tokenizer when you instantiate it (to know which padding token to use, and whether the model expects padding to be on the left or on the right of the inputs) and will do everything you need:
+
+```py
+from transformers import DataCollatorWithPadding
+
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+
+To test this new toy, let's grab a few samples from our training set that we would like to batch together. Here, we remove the columns `idx`, `sentence1`, and `sentence2` as they won't be needed and contain strings (and we can't create tensors with strings) and have a look at the lengths of each entry in the batch:
+
+```py
+samples = tokenized_datasets["train"][:8]
+samples = {k: v for k, v in samples.items() if k not in ["idx", "sentence1", "sentence2"]}
+[len(x) for x in samples["input_ids"]]
+```
+
+```python out
+[50, 59, 47, 67, 59, 50, 62, 32]
+```
+
+No surprise, we get samples of varying length, from 32 to 67. Dynamic padding means the samples in this batch should all be padded to a length of 67, the maximum length inside the batch. Without dynamic padding, all of the samples would have to be padded to the maximum length in the whole dataset, or the maximum length the model can accept. Let's double-check that our `data_collator` is dynamically padding the batch properly:
+
+```py
+batch = data_collator(samples)
+{k: v.shape for k, v in batch.items()}
+```
+
+```python out
+{'attention_mask': torch.Size([8, 67]),
+ 'input_ids': torch.Size([8, 67]),
+ 'token_type_ids': torch.Size([8, 67]),
+ 'labels': torch.Size([8])}
+```
+
+Looking good! Now that we've gone from raw text to batches our model can deal with, we're ready to fine-tune it!
+
+> [!TIP]
+> ✏️ **Try it out!** Replicate the preprocessing on the GLUE SST-2 dataset. It's a little bit different since it's composed of single sentences instead of pairs, but the rest of what we did should look the same. For a harder challenge, try to write a preprocessing function that works on any of the GLUE tasks.
+>
+> 📖 **Additional Practice**: Check out these hands-on examples from the [🤗 Transformers examples](https://huggingface.co/docs/transformers/main/en/notebooks).
+
+Perfect! Now that we have preprocessed our data with the latest best practices from the 🤗 Datasets library, we're ready to move on to training our model using the modern Trainer API. The next section will show you how to fine-tune your model effectively using the latest features and optimizations available in the Hugging Face ecosystem.
+
+## Section Quiz[[section-quiz]]
+
+Test your understanding of data processing concepts:
+
+### 1. What is the main advantage of using `Dataset.map()` with `batched=True`?
+
+
+
+### 2. Why do we use dynamic padding instead of padding all sequences to the maximum length in the dataset?
+
+
+
+### 3. What does the `token_type_ids` field represent in BERT tokenization?
+
+
+
+### 4. When loading a dataset with `load_dataset('glue', 'mrpc')`, what does the second argument specify?
+
+
+
+### 5. What is the purpose of removing columns like 'sentence1' and 'sentence2' before training?
+
+
+
+> [!TIP]
+> 💡 **Key Takeaways:**
+> - Use `batched=True` with `Dataset.map()` for significantly faster preprocessing
+> - Dynamic padding with `DataCollatorWithPadding` is more efficient than fixed-length padding
+> - Always preprocess your data to match what your model expects (numerical tensors, correct column names)
+> - The 🤗 Datasets library provides powerful tools for efficient data processing at scale
diff --git a/chapters/en/chapter3/3.mdx b/chapters/en/chapter3/3.mdx
index 20c47152c..27bc8ab51 100644
--- a/chapters/en/chapter3/3.mdx
+++ b/chapters/en/chapter3/3.mdx
@@ -1,372 +1,372 @@
-
-
-# Fine-tuning a model with the Trainer API[[fine-tuning-a-model-with-the-trainer-api]]
-
-
-
-
-
-🤗 Transformers provides a `Trainer` class to help you fine-tune any of the pretrained models it provides on your dataset with modern best practices. Once you've done all the data preprocessing work in the last section, you have just a few steps left to define the `Trainer`. The hardest part is likely to be preparing the environment to run `Trainer.train()`, as it will run very slowly on a CPU. If you don't have a GPU set up, you can get access to free GPUs or TPUs on [Google Colab](https://colab.research.google.com/).
-
-> [!TIP]
-> 📚 **Training Resources**: Before diving into training, familiarize yourself with the comprehensive [🤗 Transformers training guide](https://huggingface.co/docs/transformers/main/en/training) and explore practical examples in the [fine-tuning cookbook](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu).
-
-The code examples below assume you have already executed the examples in the previous section. Here is a short summary recapping what you need:
-
-```py
-from datasets import load_dataset
-from transformers import AutoTokenizer, DataCollatorWithPadding
-
-raw_datasets = load_dataset("glue", "mrpc")
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-
-def tokenize_function(example):
- return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
-
-
-tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
-```
-
-### Training[[training]]
-
-The first step before we can define our `Trainer` is to define a `TrainingArguments` class that will contain all the hyperparameters the `Trainer` will use for training and evaluation. The only argument you have to provide is a directory where the trained model will be saved, as well as the checkpoints along the way. For all the rest, you can leave the defaults, which should work pretty well for a basic fine-tuning.
-
-```py
-from transformers import TrainingArguments
-
-training_args = TrainingArguments("test-trainer")
-```
-
-If you want to automatically upload your model to the Hub during training, pass along `push_to_hub=True` in the `TrainingArguments`. We will learn more about this in [Chapter 4](/course/chapter4/3)
-
-> [!TIP]
-> 🚀 **Advanced Configuration**: For detailed information on all available training arguments and optimization strategies, check out the [TrainingArguments documentation](https://huggingface.co/docs/transformers/main/en/main_classes/trainer#transformers.TrainingArguments) and the [training configuration cookbook](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu).
-
-The second step is to define our model. As in the [previous chapter](/course/chapter2), we will use the `AutoModelForSequenceClassification` class, with two labels:
-
-```py
-from transformers import AutoModelForSequenceClassification
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-```
-
-You will notice that unlike in [Chapter 2](/course/chapter2), you get a warning after instantiating this pretrained model. This is because BERT has not been pretrained on classifying pairs of sentences, so the head of the pretrained model has been discarded and a new head suitable for sequence classification has been added instead. The warnings indicate that some weights were not used (the ones corresponding to the dropped pretraining head) and that some others were randomly initialized (the ones for the new head). It concludes by encouraging you to train the model, which is exactly what we are going to do now.
-
-Once we have our model, we can define a `Trainer` by passing it all the objects constructed up to now — the `model`, the `training_args`, the training and validation datasets, our `data_collator`, and our `processing_class`. The `processing_class` parameter is a newer addition that tells the Trainer which tokenizer to use for processing:
-
-```py
-from transformers import Trainer
-
-trainer = Trainer(
- model,
- training_args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- processing_class=tokenizer,
-)
-```
-
-When you pass a tokenizer as the `processing_class`, the default `data_collator` used by the `Trainer` will be a `DataCollatorWithPadding`. You can skip the `data_collator=data_collator` line in this case, but we included it here to show you this important part of the processing pipeline.
-
-> [!TIP]
-> 📖 **Learn More**: For comprehensive details on the Trainer class and its parameters, visit the [Trainer API documentation](https://huggingface.co/docs/transformers/main/en/main_classes/trainer) and explore advanced usage patterns in the [training cookbook recipes](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu).
-
-To fine-tune the model on our dataset, we just have to call the `train()` method of our `Trainer`:
-
-```py
-trainer.train()
-```
-
-This will start the fine-tuning (which should take a couple of minutes on a GPU) and report the training loss every 500 steps. It won't, however, tell you how well (or badly) your model is performing. This is because:
-
-1. We didn't tell the `Trainer` to evaluate during training by setting `eval_strategy` in `TrainingArguments` to either `"steps"` (evaluate every `eval_steps`) or `"epoch"` (evaluate at the end of each epoch).
-2. We didn't provide the `Trainer` with a `compute_metrics()` function to calculate a metric during said evaluation (otherwise the evaluation would just have printed the loss, which is not a very intuitive number).
-
-
-### Evaluation[[evaluation]]
-
-Let's see how we can build a useful `compute_metrics()` function and use it the next time we train. The function must take an `EvalPrediction` object (which is a named tuple with a `predictions` field and a `label_ids` field) and will return a dictionary mapping strings to floats (the strings being the names of the metrics returned, and the floats their values). To get some predictions from our model, we can use the `Trainer.predict()` command:
-
-```py
-predictions = trainer.predict(tokenized_datasets["validation"])
-print(predictions.predictions.shape, predictions.label_ids.shape)
-```
-
-```python out
-(408, 2) (408,)
-```
-
-The output of the `predict()` method is another named tuple with three fields: `predictions`, `label_ids`, and `metrics`. The `metrics` field will just contain the loss on the dataset passed, as well as some time metrics (how long it took to predict, in total and on average). Once we complete our `compute_metrics()` function and pass it to the `Trainer`, that field will also contain the metrics returned by `compute_metrics()`.
-
-As you can see, `predictions` is a two-dimensional array with shape 408 x 2 (408 being the number of elements in the dataset we used). Those are the logits for each element of the dataset we passed to `predict()` (as you saw in the [previous chapter](/course/chapter2), all Transformer models return logits). To transform them into predictions that we can compare to our labels, we need to take the index with the maximum value on the second axis:
-
-```py
-import numpy as np
-
-preds = np.argmax(predictions.predictions, axis=-1)
-```
-
-We can now compare those `preds` to the labels. To build our `compute_metric()` function, we will rely on the metrics from the 🤗 [Evaluate](https://github.com/huggingface/evaluate/) library. We can load the metrics associated with the MRPC dataset as easily as we loaded the dataset, this time with the `evaluate.load()` function. The object returned has a `compute()` method we can use to do the metric calculation:
-
-```py
-import evaluate
-
-metric = evaluate.load("glue", "mrpc")
-metric.compute(predictions=preds, references=predictions.label_ids)
-```
-
-```python out
-{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
-```
-
-> [!TIP]
-> Learn about different evaluation metrics and strategies in the [🤗 Evaluate documentation](https://huggingface.co/docs/evaluate/).
-
-The exact results you get may vary, as the random initialization of the model head might change the metrics it achieved. Here, we can see our model has an accuracy of 85.78% on the validation set and an F1 score of 89.97. Those are the two metrics used to evaluate results on the MRPC dataset for the GLUE benchmark. The table in the [BERT paper](https://arxiv.org/pdf/1810.04805.pdf) reported an F1 score of 88.9 for the base model. That was the `uncased` model while we are currently using the `cased` model, which explains the better result.
-
-Wrapping everything together, we get our `compute_metrics()` function:
-
-```py
-def compute_metrics(eval_preds):
- metric = evaluate.load("glue", "mrpc")
- logits, labels = eval_preds
- predictions = np.argmax(logits, axis=-1)
- return metric.compute(predictions=predictions, references=labels)
-```
-
-And to see it used in action to report metrics at the end of each epoch, here is how we define a new `Trainer` with this `compute_metrics()` function:
-
-```py
-training_args = TrainingArguments("test-trainer", eval_strategy="epoch")
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-
-trainer = Trainer(
- model,
- training_args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- processing_class=tokenizer,
- compute_metrics=compute_metrics,
-)
-```
-
-Note that we create a new `TrainingArguments` with its `eval_strategy` set to `"epoch"` and a new model — otherwise, we would just be continuing the training of the model we have already trained. To launch a new training run, we execute:
-
-```py
-trainer.train()
-```
-
-This time, it will report the validation loss and metrics at the end of each epoch on top of the training loss. Again, the exact accuracy/F1 score you reach might be a bit different from what we found, because of the random head initialization of the model, but it should be in the same ballpark.
-
-### Advanced Training Features[[advanced-training-features]]
-
-The `Trainer` comes with many built-in features that make modern deep learning best practices accessible:
-
-**Mixed Precision Training**: Use `fp16=True` in your training arguments for faster training and reduced memory usage:
-
-```py
-training_args = TrainingArguments(
- "test-trainer",
- eval_strategy="epoch",
- fp16=True, # Enable mixed precision
-)
-```
-
-**Gradient Accumulation**: For effective larger batch sizes when GPU memory is limited:
-
-```py
-training_args = TrainingArguments(
- "test-trainer",
- eval_strategy="epoch",
- per_device_train_batch_size=4,
- gradient_accumulation_steps=4, # Effective batch size = 4 * 4 = 16
-)
-```
-
-**Learning Rate Scheduling**: The Trainer uses linear decay by default, but you can customize this:
-
-```py
-training_args = TrainingArguments(
- "test-trainer",
- eval_strategy="epoch",
- learning_rate=2e-5,
- lr_scheduler_type="cosine", # Try different schedulers
-)
-```
-
-> [!TIP]
-> 🎯 **Performance Optimization**: For more advanced training techniques including distributed training, memory optimization, and hardware-specific optimizations, explore the [🤗 Transformers performance guide](https://huggingface.co/docs/transformers/main/en/performance).
-
-The `Trainer` will work out of the box on multiple GPUs or TPUs and provides lots of options for distributed training. We will go over everything it supports in Chapter 10.
-
-This concludes the introduction to fine-tuning using the `Trainer` API. An example of doing this for most common NLP tasks will be given in [Chapter 7](/course/chapter7), but for now let's look at how to do the same thing with a pure PyTorch training loop.
-
-> [!TIP]
-> 📝 **More Examples**: Check out the comprehensive collection of [🤗 Transformers notebooks](https://huggingface.co/docs/transformers/main/en/notebooks).
-
-## Section Quiz[[section-quiz]]
-
-Test your understanding of the Trainer API and fine-tuning concepts:
-
-### 1. What is the purpose of the processing_class parameter in the Trainer?
-
-
-
-### 2. Which TrainingArguments parameter controls how often evaluation occurs during training?
-
-
-
-### 3. What does fp16=True in TrainingArguments enable?
-
-
-
-### 4. What is the role of the compute_metrics function in the Trainer?
-
-
-
-### 5. What happens when you don't provide an eval_dataset to the Trainer?
-
-
-
-### 6. What is gradient accumulation and how do you enable it?
-
-
-
-> [!TIP]
-> 💡 **Key Takeaways:**
-> - The `Trainer` API provides a high-level interface that handles most training complexity
-> - Use `processing_class` to specify your tokenizer for proper data handling
-> - `TrainingArguments` controls all aspects of training: learning rate, batch size, evaluation strategy, and optimizations
-> - `compute_metrics` enables custom evaluation metrics beyond just training loss
-> - Modern features like mixed precision (`fp16=True`) and gradient accumulation can significantly improve training efficiency
-
+
+
+# Fine-tuning a model with the Trainer API[[fine-tuning-a-model-with-the-trainer-api]]
+
+
+
+
+
+🤗 Transformers provides a `Trainer` class to help you fine-tune any of the pretrained models it provides on your dataset with modern best practices. Once you've done all the data preprocessing work in the last section, you have just a few steps left to define the `Trainer`. The hardest part is likely to be preparing the environment to run `Trainer.train()`, as it will run very slowly on a CPU. If you don't have a GPU set up, you can get access to free GPUs or TPUs on [Google Colab](https://colab.research.google.com/).
+
+> [!TIP]
+> 📚 **Training Resources**: Before diving into training, familiarize yourself with the comprehensive [🤗 Transformers training guide](https://huggingface.co/docs/transformers/main/en/training) and explore practical examples in the [fine-tuning cookbook](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu).
+
+The code examples below assume you have already executed the examples in the previous section. Here is a short summary recapping what you need:
+
+```py
+from datasets import load_dataset
+from transformers import AutoTokenizer, DataCollatorWithPadding
+
+raw_datasets = load_dataset("glue", "mrpc")
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+
+
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+
+### Training[[training]]
+
+The first step before we can define our `Trainer` is to define a `TrainingArguments` class that will contain all the hyperparameters the `Trainer` will use for training and evaluation. The only argument you have to provide is a directory where the trained model will be saved, as well as the checkpoints along the way. For all the rest, you can leave the defaults, which should work pretty well for a basic fine-tuning.
+
+```py
+from transformers import TrainingArguments
+
+training_args = TrainingArguments("test-trainer")
+```
+
+If you want to automatically upload your model to the Hub during training, pass along `push_to_hub=True` in the `TrainingArguments`. We will learn more about this in [Chapter 4](/course/chapter4/3)
+
+> [!TIP]
+> 🚀 **Advanced Configuration**: For detailed information on all available training arguments and optimization strategies, check out the [TrainingArguments documentation](https://huggingface.co/docs/transformers/main/en/main_classes/trainer#transformers.TrainingArguments) and the [training configuration cookbook](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu).
+
+The second step is to define our model. As in the [previous chapter](/course/chapter2), we will use the `AutoModelForSequenceClassification` class, with two labels:
+
+```py
+from transformers import AutoModelForSequenceClassification
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+```
+
+You will notice that unlike in [Chapter 2](/course/chapter2), you get a warning after instantiating this pretrained model. This is because BERT has not been pretrained on classifying pairs of sentences, so the head of the pretrained model has been discarded and a new head suitable for sequence classification has been added instead. The warnings indicate that some weights were not used (the ones corresponding to the dropped pretraining head) and that some others were randomly initialized (the ones for the new head). It concludes by encouraging you to train the model, which is exactly what we are going to do now.
+
+Once we have our model, we can define a `Trainer` by passing it all the objects constructed up to now — the `model`, the `training_args`, the training and validation datasets, our `data_collator`, and our `processing_class`. The `processing_class` parameter is a newer addition that tells the Trainer which tokenizer to use for processing:
+
+```py
+from transformers import Trainer
+
+trainer = Trainer(
+ model,
+ training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ processing_class=tokenizer,
+)
+```
+
+When you pass a tokenizer as the `processing_class`, the default `data_collator` used by the `Trainer` will be a `DataCollatorWithPadding`. You can skip the `data_collator=data_collator` line in this case, but we included it here to show you this important part of the processing pipeline.
+
+> [!TIP]
+> 📖 **Learn More**: For comprehensive details on the Trainer class and its parameters, visit the [Trainer API documentation](https://huggingface.co/docs/transformers/main/en/main_classes/trainer) and explore advanced usage patterns in the [training cookbook recipes](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu).
+
+To fine-tune the model on our dataset, we just have to call the `train()` method of our `Trainer`:
+
+```py
+trainer.train()
+```
+
+This will start the fine-tuning (which should take a couple of minutes on a GPU) and report the training loss every 500 steps. It won't, however, tell you how well (or badly) your model is performing. This is because:
+
+1. We didn't tell the `Trainer` to evaluate during training by setting `eval_strategy` in `TrainingArguments` to either `"steps"` (evaluate every `eval_steps`) or `"epoch"` (evaluate at the end of each epoch).
+2. We didn't provide the `Trainer` with a `compute_metrics()` function to calculate a metric during said evaluation (otherwise the evaluation would just have printed the loss, which is not a very intuitive number).
+
+
+### Evaluation[[evaluation]]
+
+Let's see how we can build a useful `compute_metrics()` function and use it the next time we train. The function must take an `EvalPrediction` object (which is a named tuple with a `predictions` field and a `label_ids` field) and will return a dictionary mapping strings to floats (the strings being the names of the metrics returned, and the floats their values). To get some predictions from our model, we can use the `Trainer.predict()` command:
+
+```py
+predictions = trainer.predict(tokenized_datasets["validation"])
+print(predictions.predictions.shape, predictions.label_ids.shape)
+```
+
+```python out
+(408, 2) (408,)
+```
+
+The output of the `predict()` method is another named tuple with three fields: `predictions`, `label_ids`, and `metrics`. The `metrics` field will just contain the loss on the dataset passed, as well as some time metrics (how long it took to predict, in total and on average). Once we complete our `compute_metrics()` function and pass it to the `Trainer`, that field will also contain the metrics returned by `compute_metrics()`.
+
+As you can see, `predictions` is a two-dimensional array with shape 408 x 2 (408 being the number of elements in the dataset we used). Those are the logits for each element of the dataset we passed to `predict()` (as you saw in the [previous chapter](/course/chapter2), all Transformer models return logits). To transform them into predictions that we can compare to our labels, we need to take the index with the maximum value on the second axis:
+
+```py
+import numpy as np
+
+preds = np.argmax(predictions.predictions, axis=-1)
+```
+
+We can now compare those `preds` to the labels. To build our `compute_metric()` function, we will rely on the metrics from the 🤗 [Evaluate](https://github.com/huggingface/evaluate/) library. We can load the metrics associated with the MRPC dataset as easily as we loaded the dataset, this time with the `evaluate.load()` function. The object returned has a `compute()` method we can use to do the metric calculation:
+
+```py
+import evaluate
+
+metric = evaluate.load("glue", "mrpc")
+metric.compute(predictions=preds, references=predictions.label_ids)
+```
+
+```python out
+{'accuracy': 0.8578431372549019, 'f1': 0.8996539792387542}
+```
+
+> [!TIP]
+> Learn about different evaluation metrics and strategies in the [🤗 Evaluate documentation](https://huggingface.co/docs/evaluate/).
+
+The exact results you get may vary, as the random initialization of the model head might change the metrics it achieved. Here, we can see our model has an accuracy of 85.78% on the validation set and an F1 score of 89.97. Those are the two metrics used to evaluate results on the MRPC dataset for the GLUE benchmark. The table in the [BERT paper](https://arxiv.org/pdf/1810.04805.pdf) reported an F1 score of 88.9 for the base model. That was the `uncased` model while we are currently using the `cased` model, which explains the better result.
+
+Wrapping everything together, we get our `compute_metrics()` function:
+
+```py
+def compute_metrics(eval_preds):
+ metric = evaluate.load("glue", "mrpc")
+ logits, labels = eval_preds
+ predictions = np.argmax(logits, axis=-1)
+ return metric.compute(predictions=predictions, references=labels)
+```
+
+And to see it used in action to report metrics at the end of each epoch, here is how we define a new `Trainer` with this `compute_metrics()` function:
+
+```py
+training_args = TrainingArguments("test-trainer", eval_strategy="epoch")
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+
+trainer = Trainer(
+ model,
+ training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ processing_class=tokenizer,
+ compute_metrics=compute_metrics,
+)
+```
+
+Note that we create a new `TrainingArguments` with its `eval_strategy` set to `"epoch"` and a new model — otherwise, we would just be continuing the training of the model we have already trained. To launch a new training run, we execute:
+
+```py
+trainer.train()
+```
+
+This time, it will report the validation loss and metrics at the end of each epoch on top of the training loss. Again, the exact accuracy/F1 score you reach might be a bit different from what we found, because of the random head initialization of the model, but it should be in the same ballpark.
+
+### Advanced Training Features[[advanced-training-features]]
+
+The `Trainer` comes with many built-in features that make modern deep learning best practices accessible:
+
+**Mixed Precision Training**: Use `fp16=True` in your training arguments for faster training and reduced memory usage:
+
+```py
+training_args = TrainingArguments(
+ "test-trainer",
+ eval_strategy="epoch",
+ fp16=True, # Enable mixed precision
+)
+```
+
+**Gradient Accumulation**: For effective larger batch sizes when GPU memory is limited:
+
+```py
+training_args = TrainingArguments(
+ "test-trainer",
+ eval_strategy="epoch",
+ per_device_train_batch_size=4,
+ gradient_accumulation_steps=4, # Effective batch size = 4 * 4 = 16
+)
+```
+
+**Learning Rate Scheduling**: The Trainer uses linear decay by default, but you can customize this:
+
+```py
+training_args = TrainingArguments(
+ "test-trainer",
+ eval_strategy="epoch",
+ learning_rate=2e-5,
+ lr_scheduler_type="cosine", # Try different schedulers
+)
+```
+
+> [!TIP]
+> 🎯 **Performance Optimization**: For more advanced training techniques including distributed training, memory optimization, and hardware-specific optimizations, explore the [🤗 Transformers performance guide](https://huggingface.co/docs/transformers/main/en/performance).
+
+The `Trainer` will work out of the box on multiple GPUs or TPUs and provides lots of options for distributed training. We will go over everything it supports in Chapter 10.
+
+This concludes the introduction to fine-tuning using the `Trainer` API. An example of doing this for most common NLP tasks will be given in [Chapter 7](/course/chapter7), but for now let's look at how to do the same thing with a pure PyTorch training loop.
+
+> [!TIP]
+> 📝 **More Examples**: Check out the comprehensive collection of [🤗 Transformers notebooks](https://huggingface.co/docs/transformers/main/en/notebooks).
+
+## Section Quiz[[section-quiz]]
+
+Test your understanding of the Trainer API and fine-tuning concepts:
+
+### 1. What is the purpose of the processing_class parameter in the Trainer?
+
+
+
+### 2. Which TrainingArguments parameter controls how often evaluation occurs during training?
+
+
+
+### 3. What does fp16=True in TrainingArguments enable?
+
+
+
+### 4. What is the role of the compute_metrics function in the Trainer?
+
+
+
+### 5. What happens when you don't provide an eval_dataset to the Trainer?
+
+
+
+### 6. What is gradient accumulation and how do you enable it?
+
+
+
+> [!TIP]
+> 💡 **Key Takeaways:**
+> - The `Trainer` API provides a high-level interface that handles most training complexity
+> - Use `processing_class` to specify your tokenizer for proper data handling
+> - `TrainingArguments` controls all aspects of training: learning rate, batch size, evaluation strategy, and optimizations
+> - `compute_metrics` enables custom evaluation metrics beyond just training loss
+> - Modern features like mixed precision (`fp16=True`) and gradient accumulation can significantly improve training efficiency
+
diff --git a/chapters/en/chapter3/4.mdx b/chapters/en/chapter3/4.mdx
index 2bbcb8e57..7d1c30203 100644
--- a/chapters/en/chapter3/4.mdx
+++ b/chapters/en/chapter3/4.mdx
@@ -1,543 +1,543 @@
-# A full training loop[[a-full-training]]
-
-
-
-
-
-Now we'll see how to achieve the same results as we did in the last section without using the `Trainer` class, implementing a training loop from scratch with modern PyTorch best practices. Again, we assume you have done the data processing in section 2. Here is a short summary covering everything you will need:
-
-> [!TIP]
-> 🏗️ **Training from Scratch**: This section builds on the previous content. For comprehensive guidance on PyTorch training loops and best practices, check out the [🤗 Transformers training documentation](https://huggingface.co/docs/transformers/main/en/training#train-in-native-pytorch) and the [custom training cookbook](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu#model).
-
-```py
-from datasets import load_dataset
-from transformers import AutoTokenizer, DataCollatorWithPadding
-
-raw_datasets = load_dataset("glue", "mrpc")
-checkpoint = "bert-base-uncased"
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-
-def tokenize_function(example):
- return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
-
-
-tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
-data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
-```
-
-### Prepare for training[[prepare-for-training]]
-
-Before actually writing our training loop, we will need to define a few objects. The first ones are the dataloaders we will use to iterate over batches. But before we can define those dataloaders, we need to apply a bit of postprocessing to our `tokenized_datasets`, to take care of some things that the `Trainer` did for us automatically. Specifically, we need to:
-
-- Remove the columns corresponding to values the model does not expect (like the `sentence1` and `sentence2` columns).
-- Rename the column `label` to `labels` (because the model expects the argument to be named `labels`).
-- Set the format of the datasets so they return PyTorch tensors instead of lists.
-
-Our `tokenized_datasets` has one method for each of those steps:
-
-```py
-tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
-tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
-tokenized_datasets.set_format("torch")
-tokenized_datasets["train"].column_names
-```
-
-We can then check that the result only has columns that our model will accept:
-
-```python
-["attention_mask", "input_ids", "labels", "token_type_ids"]
-```
-
-Now that this is done, we can easily define our dataloaders:
-
-```py
-from torch.utils.data import DataLoader
-
-train_dataloader = DataLoader(
- tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
-)
-eval_dataloader = DataLoader(
- tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
-)
-```
-
-To quickly check there is no mistake in the data processing, we can inspect a batch like this:
-
-```py
-for batch in train_dataloader:
- break
-{k: v.shape for k, v in batch.items()}
-```
-
-```python out
-{'attention_mask': torch.Size([8, 65]),
- 'input_ids': torch.Size([8, 65]),
- 'labels': torch.Size([8]),
- 'token_type_ids': torch.Size([8, 65])}
-```
-
-Note that the actual shapes will probably be slightly different for you since we set `shuffle=True` for the training dataloader and we are padding to the maximum length inside the batch.
-
-Now that we're completely finished with data preprocessing (a satisfying yet elusive goal for any ML practitioner), let's turn to the model. We instantiate it exactly as we did in the previous section:
-
-```py
-from transformers import AutoModelForSequenceClassification
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-```
-
-To make sure that everything will go smoothly during training, we pass our batch to this model:
-
-```py
-outputs = model(**batch)
-print(outputs.loss, outputs.logits.shape)
-```
-
-```python out
-tensor(0.5441, grad_fn=) torch.Size([8, 2])
-```
-
-All 🤗 Transformers models will return the loss when `labels` are provided, and we also get the logits (two for each input in our batch, so a tensor of size 8 x 2).
-
-We're almost ready to write our training loop! We're just missing two things: an optimizer and a learning rate scheduler. Since we are trying to replicate what the `Trainer` was doing by hand, we will use the same defaults. The optimizer used by the `Trainer` is `AdamW`, which is the same as Adam, but with a twist for weight decay regularization (see ["Decoupled Weight Decay Regularization"](https://arxiv.org/abs/1711.05101) by Ilya Loshchilov and Frank Hutter):
-
-```py
-from torch.optim import AdamW
-
-optimizer = AdamW(model.parameters(), lr=5e-5)
-```
-
-> [!TIP]
-> 💡 **Modern Optimization Tips**: For even better performance, you can try:
-> - **AdamW with weight decay**: `AdamW(model.parameters(), lr=5e-5, weight_decay=0.01)`
-> - **8-bit Adam**: Use `bitsandbytes` for memory-efficient optimization
-> - **Different learning rates**: Lower learning rates (1e-5 to 3e-5) often work better for large models
->
-> 🚀 **Optimization Resources**: Learn more about optimizers and training strategies in the [🤗 Transformers optimization guide](https://huggingface.co/docs/transformers/main/en/performance#optimizer).
-
-Finally, the learning rate scheduler used by default is just a linear decay from the maximum value (5e-5) to 0. To properly define it, we need to know the number of training steps we will take, which is the number of epochs we want to run multiplied by the number of training batches (which is the length of our training dataloader). The `Trainer` uses three epochs by default, so we will follow that:
-
-```py
-from transformers import get_scheduler
-
-num_epochs = 3
-num_training_steps = num_epochs * len(train_dataloader)
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-print(num_training_steps)
-```
-
-```python out
-1377
-```
-
-### The training loop[[the-training-loop]]
-
-One last thing: we will want to use the GPU if we have access to one (on a CPU, training might take several hours instead of a couple of minutes). To do this, we define a `device` we will put our model and our batches on:
-
-```py
-import torch
-
-device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
-model.to(device)
-device
-```
-
-```python out
-device(type='cuda')
-```
-
-We are now ready to train! To get some sense of when training will be finished, we add a progress bar over our number of training steps, using the `tqdm` library:
-
-```py
-from tqdm.auto import tqdm
-
-progress_bar = tqdm(range(num_training_steps))
-
-model.train()
-for epoch in range(num_epochs):
- for batch in train_dataloader:
- batch = {k: v.to(device) for k, v in batch.items()}
- outputs = model(**batch)
- loss = outputs.loss
- loss.backward()
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-> [!TIP]
-> 💡 **Modern Training Optimizations**: To make your training loop even more efficient, consider:
->
-> - **Gradient Clipping**: Add `torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)` before `optimizer.step()`
-> - **Mixed Precision**: Use `torch.cuda.amp.autocast()` and `GradScaler` for faster training
-> - **Gradient Accumulation**: Accumulate gradients over multiple batches to simulate larger batch sizes
-> - **Checkpointing**: Save model checkpoints periodically to resume training if interrupted
->
-> 🔧 **Implementation Guide**: For detailed examples of these optimizations, see the [🤗 Transformers efficient training guide](https://huggingface.co/docs/transformers/main/en/perf_train_gpu_one) and the [range of optimizers](https://huggingface.co/docs/transformers/main/en/optimizers).
-
-You can see that the core of the training loop looks a lot like the one in the introduction. We didn't ask for any reporting, so this training loop will not tell us anything about how the model fares. We need to add an evaluation loop for that.
-
-
-### The evaluation loop[[the-evaluation-loop]]
-
-As we did earlier, we will use a metric provided by the 🤗 Evaluate library. We've already seen the `metric.compute()` method, but metrics can actually accumulate batches for us as we go over the prediction loop with the method `add_batch()`. Once we have accumulated all the batches, we can get the final result with `metric.compute()`. Here's how to implement all of this in an evaluation loop:
-
-> [!TIP]
-> 📊 **Evaluation Best Practices**: For more sophisticated evaluation strategies and metrics, explore the [🤗 Evaluate documentation](https://huggingface.co/docs/evaluate/) and the [comprehensive evaluation cookbook](https://github.com/huggingface/evaluation-guidebook).
-
-```py
-import evaluate
-
-metric = evaluate.load("glue", "mrpc")
-model.eval()
-for batch in eval_dataloader:
- batch = {k: v.to(device) for k, v in batch.items()}
- with torch.no_grad():
- outputs = model(**batch)
-
- logits = outputs.logits
- predictions = torch.argmax(logits, dim=-1)
- metric.add_batch(predictions=predictions, references=batch["labels"])
-
-metric.compute()
-```
-
-```python out
-{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
-```
-
-Again, your results will be slightly different because of the randomness in the model head initialization and the data shuffling, but they should be in the same ballpark.
-
-> [!TIP]
-> ✏️ **Try it out!** Modify the previous training loop to fine-tune your model on the SST-2 dataset.
-
-### Supercharge your training loop with 🤗 Accelerate[[supercharge-your-training-loop-with-accelerate]]
-
-
-
-The training loop we defined earlier works fine on a single CPU or GPU. But using the [🤗 Accelerate](https://github.com/huggingface/accelerate) library, with just a few adjustments we can enable distributed training on multiple GPUs or TPUs. 🤗 Accelerate handles the complexity of distributed training, mixed precision, and device placement automatically. Starting from the creation of the training and validation dataloaders, here is what our manual training loop looks like:
-
-> [!TIP]
-> ⚡ **Accelerate Deep Dive**: Learn everything about distributed training, mixed precision, and hardware optimization in the [🤗 Accelerate documentation](https://huggingface.co/docs/accelerate/) and explore practical examples in the [transformers documentation](https://huggingface.co/docs/transformers/main/en/accelerate).
-
-```py
-from accelerate import Accelerator
-from torch.optim import AdamW
-from transformers import AutoModelForSequenceClassification, get_scheduler
-
-accelerator = Accelerator()
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-optimizer = AdamW(model.parameters(), lr=3e-5)
-
-train_dl, eval_dl, model, optimizer = accelerator.prepare(
- train_dataloader, eval_dataloader, model, optimizer
-)
-
-num_epochs = 3
-num_training_steps = num_epochs * len(train_dl)
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-
-progress_bar = tqdm(range(num_training_steps))
-
-model.train()
-for epoch in range(num_epochs):
- for batch in train_dl:
- outputs = model(**batch)
- loss = outputs.loss
- accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-The first line to add is the import line. The second line instantiates an `Accelerator` object that will look at the environment and initialize the proper distributed setup. 🤗 Accelerate handles the device placement for you, so you can remove the lines that put the model on the device (or, if you prefer, change them to use `accelerator.device` instead of `device`).
-
-Then the main bulk of the work is done in the line that sends the dataloaders, the model, and the optimizer to `accelerator.prepare()`. This will wrap those objects in the proper container to make sure your distributed training works as intended. The remaining changes to make are removing the line that puts the batch on the `device` (again, if you want to keep this you can just change it to use `accelerator.device`) and replacing `loss.backward()` with `accelerator.backward(loss)`.
-
-> [!TIP]
-> ⚠️ In order to benefit from the speed-up offered by Cloud TPUs, we recommend padding your samples to a fixed length with the `padding="max_length"` and `max_length` arguments of the tokenizer.
-
-If you'd like to copy and paste it to play around, here's what the complete training loop looks like with 🤗 Accelerate:
-
-```py
-from accelerate import Accelerator
-from torch.optim import AdamW
-from transformers import AutoModelForSequenceClassification, get_scheduler
-
-accelerator = Accelerator()
-
-model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
-optimizer = AdamW(model.parameters(), lr=3e-5)
-
-train_dl, eval_dl, model, optimizer = accelerator.prepare(
- train_dataloader, eval_dataloader, model, optimizer
-)
-
-num_epochs = 3
-num_training_steps = num_epochs * len(train_dl)
-lr_scheduler = get_scheduler(
- "linear",
- optimizer=optimizer,
- num_warmup_steps=0,
- num_training_steps=num_training_steps,
-)
-
-progress_bar = tqdm(range(num_training_steps))
-
-model.train()
-for epoch in range(num_epochs):
- for batch in train_dl:
- outputs = model(**batch)
- loss = outputs.loss
- accelerator.backward(loss)
-
- optimizer.step()
- lr_scheduler.step()
- optimizer.zero_grad()
- progress_bar.update(1)
-```
-
-Putting this in a `train.py` script will make that script runnable on any kind of distributed setup. To try it out in your distributed setup, run the command:
-
-```bash
-accelerate config
-```
-
-which will prompt you to answer a few questions and dump your answers in a configuration file used by this command:
-
-```
-accelerate launch train.py
-```
-
-which will launch the distributed training.
-
-If you want to try this in a Notebook (for instance, to test it with TPUs on Colab), just paste the code in a `training_function()` and run a last cell with:
-
-```python
-from accelerate import notebook_launcher
-
-notebook_launcher(training_function)
-```
-
-You can find more examples in the [🤗 Accelerate repo](https://github.com/huggingface/accelerate/tree/main/examples).
-
-> [!TIP]
-> 🌐 **Distributed Training**: For comprehensive coverage of multi-GPU and multi-node training, check out the [🤗 Transformers distributed training guide](https://huggingface.co/docs/transformers/main/en/perf_train_gpu_many) and the [scaling training cookbook](https://huggingface.co/docs/transformers/main/en/accelerate).
-
-### Next Steps and Best Practices[[next-steps-and-best-practices]]
-
-Now that you've learned how to implement training from scratch, here are some additional considerations for production use:
-
-**Model Evaluation**: Always evaluate your model on multiple metrics, not just accuracy. Use the 🤗 Evaluate library for comprehensive evaluation.
-
-**Hyperparameter Tuning**: Consider using libraries like Optuna or Ray Tune for systematic hyperparameter optimization.
-
-**Model Monitoring**: Track training metrics, learning curves, and validation performance throughout training.
-
-**Model Sharing**: Once trained, share your model on the Hugging Face Hub to make it available to the community.
-
-**Efficiency**: For large models, consider techniques like gradient checkpointing, parameter-efficient fine-tuning (LoRA, AdaLoRA), or quantization methods.
-
-This concludes our deep dive into fine-tuning with custom training loops. The skills you've learned here will serve you well when you need full control over the training process or want to implement custom training logic that goes beyond what the `Trainer` API offers.
-
-## Section Quiz[[section-quiz]]
-
-Test your understanding of custom training loops and advanced training techniques:
-
-### 1. What is the main difference between Adam and AdamW optimizers?
-
-
-
-### 2. In a training loop, what is the correct order of operations?
-
-
-
-### 3. What does the 🤗 Accelerate library primarily help with?
-
-
-
-### 4. Why do we move batches to the device in a training loop?
-
-
-
-### 5. What does `model.eval()` do before evaluation?
-
-
-
-### 6. What is the purpose of `torch.no_grad()` during evaluation?
-
-
-
-### 7. What changes when you use 🤗 Accelerate in your training loop?
-
-
-
-> [!TIP]
-> 💡 **Key Takeaways:**
-> - Manual training loops give you complete control but require understanding of the proper sequence: forward → backward → optimizer step → scheduler step → zero gradients
-> - AdamW with weight decay is the recommended optimizer for transformer models
-> - Always use `model.eval()` and `torch.no_grad()` during evaluation for correct behavior and efficiency
-> - 🤗 Accelerate makes distributed training accessible with minimal code changes
-> - Device management (moving tensors to GPU/CPU) is crucial for PyTorch operations
-> - Modern techniques like mixed precision, gradient accumulation, and gradient clipping can significantly improve training efficiency
+# A full training loop[[a-full-training]]
+
+
+
+
+
+Now we'll see how to achieve the same results as we did in the last section without using the `Trainer` class, implementing a training loop from scratch with modern PyTorch best practices. Again, we assume you have done the data processing in section 2. Here is a short summary covering everything you will need:
+
+> [!TIP]
+> 🏗️ **Training from Scratch**: This section builds on the previous content. For comprehensive guidance on PyTorch training loops and best practices, check out the [🤗 Transformers training documentation](https://huggingface.co/docs/transformers/main/en/training#train-in-native-pytorch) and the [custom training cookbook](https://huggingface.co/learn/cookbook/en/fine_tuning_code_llm_on_single_gpu#model).
+
+```py
+from datasets import load_dataset
+from transformers import AutoTokenizer, DataCollatorWithPadding
+
+raw_datasets = load_dataset("glue", "mrpc")
+checkpoint = "bert-base-uncased"
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+
+def tokenize_function(example):
+ return tokenizer(example["sentence1"], example["sentence2"], truncation=True)
+
+
+tokenized_datasets = raw_datasets.map(tokenize_function, batched=True)
+data_collator = DataCollatorWithPadding(tokenizer=tokenizer)
+```
+
+### Prepare for training[[prepare-for-training]]
+
+Before actually writing our training loop, we will need to define a few objects. The first ones are the dataloaders we will use to iterate over batches. But before we can define those dataloaders, we need to apply a bit of postprocessing to our `tokenized_datasets`, to take care of some things that the `Trainer` did for us automatically. Specifically, we need to:
+
+- Remove the columns corresponding to values the model does not expect (like the `sentence1` and `sentence2` columns).
+- Rename the column `label` to `labels` (because the model expects the argument to be named `labels`).
+- Set the format of the datasets so they return PyTorch tensors instead of lists.
+
+Our `tokenized_datasets` has one method for each of those steps:
+
+```py
+tokenized_datasets = tokenized_datasets.remove_columns(["sentence1", "sentence2", "idx"])
+tokenized_datasets = tokenized_datasets.rename_column("label", "labels")
+tokenized_datasets.set_format("torch")
+tokenized_datasets["train"].column_names
+```
+
+We can then check that the result only has columns that our model will accept:
+
+```python
+["attention_mask", "input_ids", "labels", "token_type_ids"]
+```
+
+Now that this is done, we can easily define our dataloaders:
+
+```py
+from torch.utils.data import DataLoader
+
+train_dataloader = DataLoader(
+ tokenized_datasets["train"], shuffle=True, batch_size=8, collate_fn=data_collator
+)
+eval_dataloader = DataLoader(
+ tokenized_datasets["validation"], batch_size=8, collate_fn=data_collator
+)
+```
+
+To quickly check there is no mistake in the data processing, we can inspect a batch like this:
+
+```py
+for batch in train_dataloader:
+ break
+{k: v.shape for k, v in batch.items()}
+```
+
+```python out
+{'attention_mask': torch.Size([8, 65]),
+ 'input_ids': torch.Size([8, 65]),
+ 'labels': torch.Size([8]),
+ 'token_type_ids': torch.Size([8, 65])}
+```
+
+Note that the actual shapes will probably be slightly different for you since we set `shuffle=True` for the training dataloader and we are padding to the maximum length inside the batch.
+
+Now that we're completely finished with data preprocessing (a satisfying yet elusive goal for any ML practitioner), let's turn to the model. We instantiate it exactly as we did in the previous section:
+
+```py
+from transformers import AutoModelForSequenceClassification
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+```
+
+To make sure that everything will go smoothly during training, we pass our batch to this model:
+
+```py
+outputs = model(**batch)
+print(outputs.loss, outputs.logits.shape)
+```
+
+```python out
+tensor(0.5441, grad_fn=) torch.Size([8, 2])
+```
+
+All 🤗 Transformers models will return the loss when `labels` are provided, and we also get the logits (two for each input in our batch, so a tensor of size 8 x 2).
+
+We're almost ready to write our training loop! We're just missing two things: an optimizer and a learning rate scheduler. Since we are trying to replicate what the `Trainer` was doing by hand, we will use the same defaults. The optimizer used by the `Trainer` is `AdamW`, which is the same as Adam, but with a twist for weight decay regularization (see ["Decoupled Weight Decay Regularization"](https://arxiv.org/abs/1711.05101) by Ilya Loshchilov and Frank Hutter):
+
+```py
+from torch.optim import AdamW
+
+optimizer = AdamW(model.parameters(), lr=5e-5)
+```
+
+> [!TIP]
+> 💡 **Modern Optimization Tips**: For even better performance, you can try:
+> - **AdamW with weight decay**: `AdamW(model.parameters(), lr=5e-5, weight_decay=0.01)`
+> - **8-bit Adam**: Use `bitsandbytes` for memory-efficient optimization
+> - **Different learning rates**: Lower learning rates (1e-5 to 3e-5) often work better for large models
+>
+> 🚀 **Optimization Resources**: Learn more about optimizers and training strategies in the [🤗 Transformers optimization guide](https://huggingface.co/docs/transformers/main/en/performance#optimizer).
+
+Finally, the learning rate scheduler used by default is just a linear decay from the maximum value (5e-5) to 0. To properly define it, we need to know the number of training steps we will take, which is the number of epochs we want to run multiplied by the number of training batches (which is the length of our training dataloader). The `Trainer` uses three epochs by default, so we will follow that:
+
+```py
+from transformers import get_scheduler
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dataloader)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+print(num_training_steps)
+```
+
+```python out
+1377
+```
+
+### The training loop[[the-training-loop]]
+
+One last thing: we will want to use the GPU if we have access to one (on a CPU, training might take several hours instead of a couple of minutes). To do this, we define a `device` we will put our model and our batches on:
+
+```py
+import torch
+
+device = torch.device("cuda") if torch.cuda.is_available() else torch.device("cpu")
+model.to(device)
+device
+```
+
+```python out
+device(type='cuda')
+```
+
+We are now ready to train! To get some sense of when training will be finished, we add a progress bar over our number of training steps, using the `tqdm` library:
+
+```py
+from tqdm.auto import tqdm
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ outputs = model(**batch)
+ loss = outputs.loss
+ loss.backward()
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+> [!TIP]
+> 💡 **Modern Training Optimizations**: To make your training loop even more efficient, consider:
+>
+> - **Gradient Clipping**: Add `torch.nn.utils.clip_grad_norm_(model.parameters(), max_norm=1.0)` before `optimizer.step()`
+> - **Mixed Precision**: Use `torch.cuda.amp.autocast()` and `GradScaler` for faster training
+> - **Gradient Accumulation**: Accumulate gradients over multiple batches to simulate larger batch sizes
+> - **Checkpointing**: Save model checkpoints periodically to resume training if interrupted
+>
+> 🔧 **Implementation Guide**: For detailed examples of these optimizations, see the [🤗 Transformers efficient training guide](https://huggingface.co/docs/transformers/main/en/perf_train_gpu_one) and the [range of optimizers](https://huggingface.co/docs/transformers/main/en/optimizers).
+
+You can see that the core of the training loop looks a lot like the one in the introduction. We didn't ask for any reporting, so this training loop will not tell us anything about how the model fares. We need to add an evaluation loop for that.
+
+
+### The evaluation loop[[the-evaluation-loop]]
+
+As we did earlier, we will use a metric provided by the 🤗 Evaluate library. We've already seen the `metric.compute()` method, but metrics can actually accumulate batches for us as we go over the prediction loop with the method `add_batch()`. Once we have accumulated all the batches, we can get the final result with `metric.compute()`. Here's how to implement all of this in an evaluation loop:
+
+> [!TIP]
+> 📊 **Evaluation Best Practices**: For more sophisticated evaluation strategies and metrics, explore the [🤗 Evaluate documentation](https://huggingface.co/docs/evaluate/) and the [comprehensive evaluation cookbook](https://github.com/huggingface/evaluation-guidebook).
+
+```py
+import evaluate
+
+metric = evaluate.load("glue", "mrpc")
+model.eval()
+for batch in eval_dataloader:
+ batch = {k: v.to(device) for k, v in batch.items()}
+ with torch.no_grad():
+ outputs = model(**batch)
+
+ logits = outputs.logits
+ predictions = torch.argmax(logits, dim=-1)
+ metric.add_batch(predictions=predictions, references=batch["labels"])
+
+metric.compute()
+```
+
+```python out
+{'accuracy': 0.8431372549019608, 'f1': 0.8907849829351535}
+```
+
+Again, your results will be slightly different because of the randomness in the model head initialization and the data shuffling, but they should be in the same ballpark.
+
+> [!TIP]
+> ✏️ **Try it out!** Modify the previous training loop to fine-tune your model on the SST-2 dataset.
+
+### Supercharge your training loop with 🤗 Accelerate[[supercharge-your-training-loop-with-accelerate]]
+
+
+
+The training loop we defined earlier works fine on a single CPU or GPU. But using the [🤗 Accelerate](https://github.com/huggingface/accelerate) library, with just a few adjustments we can enable distributed training on multiple GPUs or TPUs. 🤗 Accelerate handles the complexity of distributed training, mixed precision, and device placement automatically. Starting from the creation of the training and validation dataloaders, here is what our manual training loop looks like:
+
+> [!TIP]
+> ⚡ **Accelerate Deep Dive**: Learn everything about distributed training, mixed precision, and hardware optimization in the [🤗 Accelerate documentation](https://huggingface.co/docs/accelerate/) and explore practical examples in the [transformers documentation](https://huggingface.co/docs/transformers/main/en/accelerate).
+
+```py
+from accelerate import Accelerator
+from torch.optim import AdamW
+from transformers import AutoModelForSequenceClassification, get_scheduler
+
+accelerator = Accelerator()
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+optimizer = AdamW(model.parameters(), lr=3e-5)
+
+train_dl, eval_dl, model, optimizer = accelerator.prepare(
+ train_dataloader, eval_dataloader, model, optimizer
+)
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dl)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dl:
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+The first line to add is the import line. The second line instantiates an `Accelerator` object that will look at the environment and initialize the proper distributed setup. 🤗 Accelerate handles the device placement for you, so you can remove the lines that put the model on the device (or, if you prefer, change them to use `accelerator.device` instead of `device`).
+
+Then the main bulk of the work is done in the line that sends the dataloaders, the model, and the optimizer to `accelerator.prepare()`. This will wrap those objects in the proper container to make sure your distributed training works as intended. The remaining changes to make are removing the line that puts the batch on the `device` (again, if you want to keep this you can just change it to use `accelerator.device`) and replacing `loss.backward()` with `accelerator.backward(loss)`.
+
+> [!TIP]
+> ⚠️ In order to benefit from the speed-up offered by Cloud TPUs, we recommend padding your samples to a fixed length with the `padding="max_length"` and `max_length` arguments of the tokenizer.
+
+If you'd like to copy and paste it to play around, here's what the complete training loop looks like with 🤗 Accelerate:
+
+```py
+from accelerate import Accelerator
+from torch.optim import AdamW
+from transformers import AutoModelForSequenceClassification, get_scheduler
+
+accelerator = Accelerator()
+
+model = AutoModelForSequenceClassification.from_pretrained(checkpoint, num_labels=2)
+optimizer = AdamW(model.parameters(), lr=3e-5)
+
+train_dl, eval_dl, model, optimizer = accelerator.prepare(
+ train_dataloader, eval_dataloader, model, optimizer
+)
+
+num_epochs = 3
+num_training_steps = num_epochs * len(train_dl)
+lr_scheduler = get_scheduler(
+ "linear",
+ optimizer=optimizer,
+ num_warmup_steps=0,
+ num_training_steps=num_training_steps,
+)
+
+progress_bar = tqdm(range(num_training_steps))
+
+model.train()
+for epoch in range(num_epochs):
+ for batch in train_dl:
+ outputs = model(**batch)
+ loss = outputs.loss
+ accelerator.backward(loss)
+
+ optimizer.step()
+ lr_scheduler.step()
+ optimizer.zero_grad()
+ progress_bar.update(1)
+```
+
+Putting this in a `train.py` script will make that script runnable on any kind of distributed setup. To try it out in your distributed setup, run the command:
+
+```bash
+accelerate config
+```
+
+which will prompt you to answer a few questions and dump your answers in a configuration file used by this command:
+
+```
+accelerate launch train.py
+```
+
+which will launch the distributed training.
+
+If you want to try this in a Notebook (for instance, to test it with TPUs on Colab), just paste the code in a `training_function()` and run a last cell with:
+
+```python
+from accelerate import notebook_launcher
+
+notebook_launcher(training_function)
+```
+
+You can find more examples in the [🤗 Accelerate repo](https://github.com/huggingface/accelerate/tree/main/examples).
+
+> [!TIP]
+> 🌐 **Distributed Training**: For comprehensive coverage of multi-GPU and multi-node training, check out the [🤗 Transformers distributed training guide](https://huggingface.co/docs/transformers/main/en/perf_train_gpu_many) and the [scaling training cookbook](https://huggingface.co/docs/transformers/main/en/accelerate).
+
+### Next Steps and Best Practices[[next-steps-and-best-practices]]
+
+Now that you've learned how to implement training from scratch, here are some additional considerations for production use:
+
+**Model Evaluation**: Always evaluate your model on multiple metrics, not just accuracy. Use the 🤗 Evaluate library for comprehensive evaluation.
+
+**Hyperparameter Tuning**: Consider using libraries like Optuna or Ray Tune for systematic hyperparameter optimization.
+
+**Model Monitoring**: Track training metrics, learning curves, and validation performance throughout training.
+
+**Model Sharing**: Once trained, share your model on the Hugging Face Hub to make it available to the community.
+
+**Efficiency**: For large models, consider techniques like gradient checkpointing, parameter-efficient fine-tuning (LoRA, AdaLoRA), or quantization methods.
+
+This concludes our deep dive into fine-tuning with custom training loops. The skills you've learned here will serve you well when you need full control over the training process or want to implement custom training logic that goes beyond what the `Trainer` API offers.
+
+## Section Quiz[[section-quiz]]
+
+Test your understanding of custom training loops and advanced training techniques:
+
+### 1. What is the main difference between Adam and AdamW optimizers?
+
+
+
+### 2. In a training loop, what is the correct order of operations?
+
+
+
+### 3. What does the 🤗 Accelerate library primarily help with?
+
+
+
+### 4. Why do we move batches to the device in a training loop?
+
+
+
+### 5. What does `model.eval()` do before evaluation?
+
+
+
+### 6. What is the purpose of `torch.no_grad()` during evaluation?
+
+
+
+### 7. What changes when you use 🤗 Accelerate in your training loop?
+
+
+
+> [!TIP]
+> 💡 **Key Takeaways:**
+> - Manual training loops give you complete control but require understanding of the proper sequence: forward → backward → optimizer step → scheduler step → zero gradients
+> - AdamW with weight decay is the recommended optimizer for transformer models
+> - Always use `model.eval()` and `torch.no_grad()` during evaluation for correct behavior and efficiency
+> - 🤗 Accelerate makes distributed training accessible with minimal code changes
+> - Device management (moving tensors to GPU/CPU) is crucial for PyTorch operations
+> - Modern techniques like mixed precision, gradient accumulation, and gradient clipping can significantly improve training efficiency
diff --git a/chapters/en/chapter3/5.mdx b/chapters/en/chapter3/5.mdx
index c402879c4..4fd747e24 100644
--- a/chapters/en/chapter3/5.mdx
+++ b/chapters/en/chapter3/5.mdx
@@ -1,408 +1,408 @@
-# Understanding Learning Curves[[understanding-learning-curves]]
-
-
-
-Now that you've learned how to implement fine-tuning using both the `Trainer` API and custom training loops, it's crucial to understand how to interpret the results. Learning curves are invaluable tools that help you evaluate your model's performance during training and identify potential issues before they reduce performance.
-
-In this section, we'll explore how to read and interpret accuracy and loss curves, understand what different curve shapes tell us about our model's behavior, and learn how to address common training issues.
-
-## What are Learning Curves?[[what-are-learning-curves]]
-
-Learning curves are visual representations of your model's performance metrics over time during training. The two most important curves to monitor are:
-
-- **Loss curves**: Show how the model's error (loss) changes over training steps or epochs
-- **Accuracy curves**: Show the percentage of correct predictions over training steps or epochs
-
-These curves help us understand whether our model is learning effectively and can guide us in making adjustments to improve performance. In Transformers, these metrics are individually computed for each batch and then logged to the disk. We can then use libraries like [Weights & Biases](https://wandb.ai/) to visualize these curves and track our model's performance over time.
-
-### Loss Curves[[loss-curves]]
-
-The loss curve shows how the model's error decreases over time. In a typical successful training run, you'll see a curve similar to the one below:
-
-
-
-- **High initial loss**: The model starts without optimization, so predictions are initially poor
-- **Decreasing loss**: As training progresses, the loss should generally decrease
-- **Convergence**: Eventually, the loss stabilizes at a low value, indicating that the model has learned the patterns in the data
-
-As in previous chapters, we can use the `Trainer` API to track these metrics and visualize them in a dashboard. Below is an example of how to do this with Weights & Biases.
-
-```python
-# Example of tracking loss during training with the Trainer
-from transformers import Trainer, TrainingArguments
-import wandb
-
-# Initialize Weights & Biases for experiment tracking
-wandb.init(project="transformer-fine-tuning", name="bert-mrpc-analysis")
-
-training_args = TrainingArguments(
- output_dir="./results",
- eval_strategy="steps",
- eval_steps=50,
- save_steps=100,
- logging_steps=10, # Log metrics every 10 steps
- num_train_epochs=3,
- per_device_train_batch_size=16,
- per_device_eval_batch_size=16,
- report_to="wandb", # Send logs to Weights & Biases
-)
-
-trainer = Trainer(
- model=model,
- args=training_args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- processing_class=tokenizer,
- compute_metrics=compute_metrics,
-)
-
-# Train and automatically log metrics
-trainer.train()
-```
-
-### Accuracy Curves[[accuracy-curves]]
-
-The accuracy curve shows the percentage of correct predictions over time. Unlike loss curves, accuracy curves should generally increase as the model learns and can typically include more steps than the loss curve.
-
-
-
-- **Start low**: Initial accuracy should be low, as the model has not yet learned the patterns in the data
-- **Increase with training**: Accuracy should generally improve as the model learns if it is able to learn the patterns in the data
-- **May show plateaus**: Accuracy often increases in discrete jumps rather than smoothly, as the model makes predictions that are close to the true labels
-
-> [!TIP]
-> 💡 **Why Accuracy Curves Are "Steppy"**: Unlike loss, which is continuous, accuracy is calculated by comparing discrete predictions to true labels. Small improvements in model confidence might not change the final prediction, causing accuracy to remain flat until a threshold is crossed.
-
-### Convergence[[convergence]]
-
-Convergence occurs when the model's performance stabilizes and the loss and accuracy curves level off. This is a sign that the model has learned the patterns in the data and is ready to be used. In simple terms, we are aiming for the model to converge to a stable performance every time we train it.
-
-
-
-Once models have converged, we can use them to make predictions on new data and refer to evaluation metrics to understand how well the model is performing.
-
-## Interpreting Learning Curve Patterns[[interpreting-learning-curve-patterns]]
-
-Different curve shapes reveal different aspects of your model's training. Let's examine the most common patterns and what they mean.
-
-### Healthy Learning Curves[[healthy-learning-curves]]
-
-A well-behaved training run typically shows curve shapes similar to the one below:
-
-
-
-Let's look at the illustration above. It displays both the loss curve (on the left) and the corresponding accuracy curve (on the right). These curves have distinct characteristics.
-
-The loss curve shows the value of the model's loss over time. Initially, the loss is high and then it gradually decreases, indicating that the model is improving. A decrease in the loss value suggests that the model is making better predictions, as the loss represents the error between the predicted output and the true output.
-
-Now let's shift our focus to the accuracy curve. It represents the model's accuracy over time. The accuracy curve begins at a low value and increases as training progresses. Accuracy measures the proportion of correctly classified instances. So, as the accuracy curve rises, it signifies that the model is making more correct predictions.
-
-One notable difference between the curves is the smoothness and the presence of "plateaus" on the accuracy curve. While the loss decreases smoothly, the plateaus on the accuracy curve indicate discrete jumps in accuracy instead of a continuous increase. This behavior is attributed to how accuracy is measured. The loss can improve if the model's output gets closer to the target, even if the final prediction is still incorrect. Accuracy, however, only improves when the prediction crosses the threshold to be correct.
-
-For example, in a binary classifier distinguishing cats (0) from dogs (1), if the model predicts 0.3 for an image of a dog (true value 1), this is rounded to 0 and is an incorrect classification. If in the next step it predicts 0.4, it's still incorrect. The loss will have decreased because 0.4 is closer to 1 than 0.3, but the accuracy remains unchanged, creating a plateau. The accuracy will only jump up when the model predicts a value greater than 0.5 that gets rounded to 1.
-
-> [!TIP]
-> **Characteristics of healthy curves:**
-> - **Smooth decline in loss**: Both training and validation loss decrease steadily
-> - **Close training/validation performance**: Small gap between training and validation metrics
-> - **Convergence**: Curves level off, indicating the model has learned the patterns
-
-### Practical Examples[[practical-examples]]
-
-Let's work through some practical examples of learning curves. First, we will highlight some approaches to monitor the learning curves during training. Below, we will break down the different patterns that can be observed in the learning curves.
-
-#### During Training[[during-training]]
-
-During the training process (after you've hit `trainer.train()`), you can monitor these key indicators:
-
-1. **Loss convergence**: Is the loss still decreasing or has it plateaued?
-2. **Overfitting signs**: Is validation loss starting to increase while training loss decreases?
-3. **Learning rate**: Are the curves too erratic (LR too high) or too flat (LR too low)?
-4. **Stability**: Are there sudden spikes or drops that indicate problems?
-
-#### After Training[[after-training]]
-
-After the training process is complete, you can analyze the complete curves to understand the model's performance.
-
-1. **Final performance**: Did the model reach acceptable performance levels?
-2. **Efficiency**: Could the same performance be achieved with fewer epochs?
-3. **Generalization**: How close are training and validation performance?
-4. **Trends**: Would additional training likely improve performance?
-
-> [!TIP]
-> 🔍 **W&B Dashboard Features**: Weights & Biases automatically creates beautiful, interactive plots of your learning curves. You can:
-> - Compare multiple runs side by side
-> - Add custom metrics and visualizations
-> - Set up alerts for anomalous behavior
-> - Share results with your team
->
-> Learn more in the [Weights & Biases documentation](https://docs.wandb.ai/).
-
-#### Overfitting[[overfitting]]
-
-Overfitting occurs when the model learns too much from the training data and is unable to generalize to different data (represented by the validation set).
-
-
-
-**Symptoms:**
-
-- Training loss continues to decrease while validation loss increases or plateaus
-- Large gap between training and validation accuracy
-- Training accuracy much higher than validation accuracy
-
-**Solutions for overfitting:**
-- **Regularization**: Add dropout, weight decay, or other regularization techniques
-- **Early stopping**: Stop training when validation performance stops improving
-- **Data augmentation**: Increase training data diversity
-- **Reduce model complexity**: Use a smaller model or fewer parameters
-
-In the sample below, we use early stopping to prevent overfitting. We set the `early_stopping_patience` to 3, which means that if the validation loss does not improve for 3 consecutive epochs, the training will be stopped.
-
-```python
-# Example of detecting overfitting with early stopping
-from transformers import EarlyStoppingCallback
-
-training_args = TrainingArguments(
- output_dir="./results",
- eval_strategy="steps",
- eval_steps=100,
- save_strategy="steps",
- save_steps=100,
- load_best_model_at_end=True,
- metric_for_best_model="eval_loss",
- greater_is_better=False,
- num_train_epochs=10, # Set high, but we'll stop early
-)
-
-# Add early stopping to prevent overfitting
-trainer = Trainer(
- model=model,
- args=training_args,
- train_dataset=tokenized_datasets["train"],
- eval_dataset=tokenized_datasets["validation"],
- data_collator=data_collator,
- processing_class=tokenizer,
- compute_metrics=compute_metrics,
- callbacks=[EarlyStoppingCallback(early_stopping_patience=3)],
-)
-```
-
-#### 2. Underfitting[[underfitting]]
-
-Underfitting occurs when the model is too simple to capture the underlying patterns in the data. This can happen for several reasons:
-
-- The model is too small or lacks capacity to learn the patterns
-- The learning rate is too low, causing slow learning
-- The dataset is too small or not representative of the problem
-- The model is not properly regularized
-
-
-
-**Symptoms:**
-- Both training and validation loss remain high
-- Model performance plateaus early in training
-- Training accuracy is lower than expected
-
-**Solutions for underfitting:**
-- **Increase model capacity**: Use a larger model or more parameters
-- **Train longer**: Increase the number of epochs
-- **Adjust learning rate**: Try different learning rates
-- **Check data quality**: Ensure your data is properly preprocessed
-
-In the sample below, we train for more epochs to see if the model can learn the patterns in the data.
-
-```python
-from transformers import TrainingArguments
-
-training_args = TrainingArguments(
- output_dir="./results",
- -num_train_epochs=5,
- +num_train_epochs=10,
-)
-```
-
-#### 3. Erratic Learning Curves[[erratic-learning-curves]]
-
-Erratic learning curves occur when the model is not learning effectively. This can happen for several reasons:
-
-- The learning rate is too high, causing the model to overshoot the optimal parameters
-- The batch size is too small, causing the model to learn slowly
-- The model is not properly regularized, causing it to overfit to the training data
-- The dataset is not properly preprocessed, causing the model to learn from noise
-
-
-
-**Symptoms:**
-- Frequent fluctuations in loss or accuracy
-- Curves show high variance or instability
-- Performance oscillates without clear trend
-
-Both training and validation curves show erratic behavior.
-
-
-
-**Solutions for erratic curves:**
-- **Lower learning rate**: Reduce step size for more stable training
-- **Increase batch size**: Larger batches provide more stable gradients
-- **Gradient clipping**: Prevent exploding gradients
-- **Better data preprocessing**: Ensure consistent data quality
-
-In the sample below, we lower the learning rate and increase the batch size.
-
-```python
-from transformers import TrainingArguments
-
-training_args = TrainingArguments(
- output_dir="./results",
- -learning_rate=1e-5,
- +learning_rate=1e-4,
- -per_device_train_batch_size=16,
- +per_device_train_batch_size=32,
-)
-```
-
-## Key Takeaways[[key-takeaways]]
-
-Understanding learning curves is crucial for becoming an effective machine learning practitioner. These visual tools provide immediate feedback about your model's training progress and help you make informed decisions about when to stop training, adjust hyperparameters, or try different approaches. With practice, you'll develop an intuitive understanding of what healthy learning curves look like and how to address issues when they arise.
-
-> [!TIP]
-> 💡 **Key Takeaways:**
-> - Learning curves are essential tools for understanding model training progress
-> - Monitor both loss and accuracy curves, but remember they have different characteristics
-> - Overfitting shows as diverging training/validation performance
-> - Underfitting shows as poor performance on both training and validation data
-> - Tools like Weights & Biases make it easy to track and analyze learning curves
-> - Early stopping and proper regularization can address most common training issues
->
-> 🔬 **Next Steps**: Practice analyzing learning curves on your own fine-tuning experiments. Try different hyperparameters and observe how they affect the curve shapes. This hands-on experience is the best way to develop intuition for reading training progress.
-
-## Section Quiz[[section-quiz]]
-
-Test your understanding of learning curves and training analysis:
-
-### 1. What does it typically mean when training loss decreases but validation loss starts increasing?
-
-
-
-### 2. Why do accuracy curves often show a "steppy" or plateau-like pattern rather than smooth increases?
-
-
-
-### 3. What is the best approach when you observe erratic, highly fluctuating learning curves?
-
-
-
-### 4. When should you consider using early stopping?
-
-
-
-### 5. What indicates that your model might be underfitting?
-
-
-
+# Understanding Learning Curves[[understanding-learning-curves]]
+
+
+
+Now that you've learned how to implement fine-tuning using both the `Trainer` API and custom training loops, it's crucial to understand how to interpret the results. Learning curves are invaluable tools that help you evaluate your model's performance during training and identify potential issues before they reduce performance.
+
+In this section, we'll explore how to read and interpret accuracy and loss curves, understand what different curve shapes tell us about our model's behavior, and learn how to address common training issues.
+
+## What are Learning Curves?[[what-are-learning-curves]]
+
+Learning curves are visual representations of your model's performance metrics over time during training. The two most important curves to monitor are:
+
+- **Loss curves**: Show how the model's error (loss) changes over training steps or epochs
+- **Accuracy curves**: Show the percentage of correct predictions over training steps or epochs
+
+These curves help us understand whether our model is learning effectively and can guide us in making adjustments to improve performance. In Transformers, these metrics are individually computed for each batch and then logged to the disk. We can then use libraries like [Weights & Biases](https://wandb.ai/) to visualize these curves and track our model's performance over time.
+
+### Loss Curves[[loss-curves]]
+
+The loss curve shows how the model's error decreases over time. In a typical successful training run, you'll see a curve similar to the one below:
+
+
+
+- **High initial loss**: The model starts without optimization, so predictions are initially poor
+- **Decreasing loss**: As training progresses, the loss should generally decrease
+- **Convergence**: Eventually, the loss stabilizes at a low value, indicating that the model has learned the patterns in the data
+
+As in previous chapters, we can use the `Trainer` API to track these metrics and visualize them in a dashboard. Below is an example of how to do this with Weights & Biases.
+
+```python
+# Example of tracking loss during training with the Trainer
+from transformers import Trainer, TrainingArguments
+import wandb
+
+# Initialize Weights & Biases for experiment tracking
+wandb.init(project="transformer-fine-tuning", name="bert-mrpc-analysis")
+
+training_args = TrainingArguments(
+ output_dir="./results",
+ eval_strategy="steps",
+ eval_steps=50,
+ save_steps=100,
+ logging_steps=10, # Log metrics every 10 steps
+ num_train_epochs=3,
+ per_device_train_batch_size=16,
+ per_device_eval_batch_size=16,
+ report_to="wandb", # Send logs to Weights & Biases
+)
+
+trainer = Trainer(
+ model=model,
+ args=training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ processing_class=tokenizer,
+ compute_metrics=compute_metrics,
+)
+
+# Train and automatically log metrics
+trainer.train()
+```
+
+### Accuracy Curves[[accuracy-curves]]
+
+The accuracy curve shows the percentage of correct predictions over time. Unlike loss curves, accuracy curves should generally increase as the model learns and can typically include more steps than the loss curve.
+
+
+
+- **Start low**: Initial accuracy should be low, as the model has not yet learned the patterns in the data
+- **Increase with training**: Accuracy should generally improve as the model learns if it is able to learn the patterns in the data
+- **May show plateaus**: Accuracy often increases in discrete jumps rather than smoothly, as the model makes predictions that are close to the true labels
+
+> [!TIP]
+> 💡 **Why Accuracy Curves Are "Steppy"**: Unlike loss, which is continuous, accuracy is calculated by comparing discrete predictions to true labels. Small improvements in model confidence might not change the final prediction, causing accuracy to remain flat until a threshold is crossed.
+
+### Convergence[[convergence]]
+
+Convergence occurs when the model's performance stabilizes and the loss and accuracy curves level off. This is a sign that the model has learned the patterns in the data and is ready to be used. In simple terms, we are aiming for the model to converge to a stable performance every time we train it.
+
+
+
+Once models have converged, we can use them to make predictions on new data and refer to evaluation metrics to understand how well the model is performing.
+
+## Interpreting Learning Curve Patterns[[interpreting-learning-curve-patterns]]
+
+Different curve shapes reveal different aspects of your model's training. Let's examine the most common patterns and what they mean.
+
+### Healthy Learning Curves[[healthy-learning-curves]]
+
+A well-behaved training run typically shows curve shapes similar to the one below:
+
+
+
+Let's look at the illustration above. It displays both the loss curve (on the left) and the corresponding accuracy curve (on the right). These curves have distinct characteristics.
+
+The loss curve shows the value of the model's loss over time. Initially, the loss is high and then it gradually decreases, indicating that the model is improving. A decrease in the loss value suggests that the model is making better predictions, as the loss represents the error between the predicted output and the true output.
+
+Now let's shift our focus to the accuracy curve. It represents the model's accuracy over time. The accuracy curve begins at a low value and increases as training progresses. Accuracy measures the proportion of correctly classified instances. So, as the accuracy curve rises, it signifies that the model is making more correct predictions.
+
+One notable difference between the curves is the smoothness and the presence of "plateaus" on the accuracy curve. While the loss decreases smoothly, the plateaus on the accuracy curve indicate discrete jumps in accuracy instead of a continuous increase. This behavior is attributed to how accuracy is measured. The loss can improve if the model's output gets closer to the target, even if the final prediction is still incorrect. Accuracy, however, only improves when the prediction crosses the threshold to be correct.
+
+For example, in a binary classifier distinguishing cats (0) from dogs (1), if the model predicts 0.3 for an image of a dog (true value 1), this is rounded to 0 and is an incorrect classification. If in the next step it predicts 0.4, it's still incorrect. The loss will have decreased because 0.4 is closer to 1 than 0.3, but the accuracy remains unchanged, creating a plateau. The accuracy will only jump up when the model predicts a value greater than 0.5 that gets rounded to 1.
+
+> [!TIP]
+> **Characteristics of healthy curves:**
+> - **Smooth decline in loss**: Both training and validation loss decrease steadily
+> - **Close training/validation performance**: Small gap between training and validation metrics
+> - **Convergence**: Curves level off, indicating the model has learned the patterns
+
+### Practical Examples[[practical-examples]]
+
+Let's work through some practical examples of learning curves. First, we will highlight some approaches to monitor the learning curves during training. Below, we will break down the different patterns that can be observed in the learning curves.
+
+#### During Training[[during-training]]
+
+During the training process (after you've hit `trainer.train()`), you can monitor these key indicators:
+
+1. **Loss convergence**: Is the loss still decreasing or has it plateaued?
+2. **Overfitting signs**: Is validation loss starting to increase while training loss decreases?
+3. **Learning rate**: Are the curves too erratic (LR too high) or too flat (LR too low)?
+4. **Stability**: Are there sudden spikes or drops that indicate problems?
+
+#### After Training[[after-training]]
+
+After the training process is complete, you can analyze the complete curves to understand the model's performance.
+
+1. **Final performance**: Did the model reach acceptable performance levels?
+2. **Efficiency**: Could the same performance be achieved with fewer epochs?
+3. **Generalization**: How close are training and validation performance?
+4. **Trends**: Would additional training likely improve performance?
+
+> [!TIP]
+> 🔍 **W&B Dashboard Features**: Weights & Biases automatically creates beautiful, interactive plots of your learning curves. You can:
+> - Compare multiple runs side by side
+> - Add custom metrics and visualizations
+> - Set up alerts for anomalous behavior
+> - Share results with your team
+>
+> Learn more in the [Weights & Biases documentation](https://docs.wandb.ai/).
+
+#### Overfitting[[overfitting]]
+
+Overfitting occurs when the model learns too much from the training data and is unable to generalize to different data (represented by the validation set).
+
+
+
+**Symptoms:**
+
+- Training loss continues to decrease while validation loss increases or plateaus
+- Large gap between training and validation accuracy
+- Training accuracy much higher than validation accuracy
+
+**Solutions for overfitting:**
+- **Regularization**: Add dropout, weight decay, or other regularization techniques
+- **Early stopping**: Stop training when validation performance stops improving
+- **Data augmentation**: Increase training data diversity
+- **Reduce model complexity**: Use a smaller model or fewer parameters
+
+In the sample below, we use early stopping to prevent overfitting. We set the `early_stopping_patience` to 3, which means that if the validation loss does not improve for 3 consecutive epochs, the training will be stopped.
+
+```python
+# Example of detecting overfitting with early stopping
+from transformers import EarlyStoppingCallback
+
+training_args = TrainingArguments(
+ output_dir="./results",
+ eval_strategy="steps",
+ eval_steps=100,
+ save_strategy="steps",
+ save_steps=100,
+ load_best_model_at_end=True,
+ metric_for_best_model="eval_loss",
+ greater_is_better=False,
+ num_train_epochs=10, # Set high, but we'll stop early
+)
+
+# Add early stopping to prevent overfitting
+trainer = Trainer(
+ model=model,
+ args=training_args,
+ train_dataset=tokenized_datasets["train"],
+ eval_dataset=tokenized_datasets["validation"],
+ data_collator=data_collator,
+ processing_class=tokenizer,
+ compute_metrics=compute_metrics,
+ callbacks=[EarlyStoppingCallback(early_stopping_patience=3)],
+)
+```
+
+#### 2. Underfitting[[underfitting]]
+
+Underfitting occurs when the model is too simple to capture the underlying patterns in the data. This can happen for several reasons:
+
+- The model is too small or lacks capacity to learn the patterns
+- The learning rate is too low, causing slow learning
+- The dataset is too small or not representative of the problem
+- The model is not properly regularized
+
+
+
+**Symptoms:**
+- Both training and validation loss remain high
+- Model performance plateaus early in training
+- Training accuracy is lower than expected
+
+**Solutions for underfitting:**
+- **Increase model capacity**: Use a larger model or more parameters
+- **Train longer**: Increase the number of epochs
+- **Adjust learning rate**: Try different learning rates
+- **Check data quality**: Ensure your data is properly preprocessed
+
+In the sample below, we train for more epochs to see if the model can learn the patterns in the data.
+
+```python
+from transformers import TrainingArguments
+
+training_args = TrainingArguments(
+ output_dir="./results",
+ -num_train_epochs=5,
+ +num_train_epochs=10,
+)
+```
+
+#### 3. Erratic Learning Curves[[erratic-learning-curves]]
+
+Erratic learning curves occur when the model is not learning effectively. This can happen for several reasons:
+
+- The learning rate is too high, causing the model to overshoot the optimal parameters
+- The batch size is too small, causing the model to learn slowly
+- The model is not properly regularized, causing it to overfit to the training data
+- The dataset is not properly preprocessed, causing the model to learn from noise
+
+
+
+**Symptoms:**
+- Frequent fluctuations in loss or accuracy
+- Curves show high variance or instability
+- Performance oscillates without clear trend
+
+Both training and validation curves show erratic behavior.
+
+
+
+**Solutions for erratic curves:**
+- **Lower learning rate**: Reduce step size for more stable training
+- **Increase batch size**: Larger batches provide more stable gradients
+- **Gradient clipping**: Prevent exploding gradients
+- **Better data preprocessing**: Ensure consistent data quality
+
+In the sample below, we lower the learning rate and increase the batch size.
+
+```python
+from transformers import TrainingArguments
+
+training_args = TrainingArguments(
+ output_dir="./results",
+ -learning_rate=1e-5,
+ +learning_rate=1e-4,
+ -per_device_train_batch_size=16,
+ +per_device_train_batch_size=32,
+)
+```
+
+## Key Takeaways[[key-takeaways]]
+
+Understanding learning curves is crucial for becoming an effective machine learning practitioner. These visual tools provide immediate feedback about your model's training progress and help you make informed decisions about when to stop training, adjust hyperparameters, or try different approaches. With practice, you'll develop an intuitive understanding of what healthy learning curves look like and how to address issues when they arise.
+
+> [!TIP]
+> 💡 **Key Takeaways:**
+> - Learning curves are essential tools for understanding model training progress
+> - Monitor both loss and accuracy curves, but remember they have different characteristics
+> - Overfitting shows as diverging training/validation performance
+> - Underfitting shows as poor performance on both training and validation data
+> - Tools like Weights & Biases make it easy to track and analyze learning curves
+> - Early stopping and proper regularization can address most common training issues
+>
+> 🔬 **Next Steps**: Practice analyzing learning curves on your own fine-tuning experiments. Try different hyperparameters and observe how they affect the curve shapes. This hands-on experience is the best way to develop intuition for reading training progress.
+
+## Section Quiz[[section-quiz]]
+
+Test your understanding of learning curves and training analysis:
+
+### 1. What does it typically mean when training loss decreases but validation loss starts increasing?
+
+
+
+### 2. Why do accuracy curves often show a "steppy" or plateau-like pattern rather than smooth increases?
+
+
+
+### 3. What is the best approach when you observe erratic, highly fluctuating learning curves?
+
+
+
+### 4. When should you consider using early stopping?
+
+
+
+### 5. What indicates that your model might be underfitting?
+
+
+
diff --git a/chapters/en/chapter3/6.mdx b/chapters/en/chapter3/6.mdx
index cb05477c1..a7c7a3fbd 100644
--- a/chapters/en/chapter3/6.mdx
+++ b/chapters/en/chapter3/6.mdx
@@ -1,40 +1,40 @@
-
-
-# Fine-tuning, Check![[fine-tuning-check]]
-
-
-
-That was comprehensive! In the first two chapters you learned about models and tokenizers, and now you know how to fine-tune them for your own data using modern best practices. To recap, in this chapter you:
-
-* Learned about datasets on the [Hub](https://huggingface.co/datasets) and modern data processing techniques
-* Learned how to load and preprocess datasets efficiently, including using dynamic padding and data collators
-* Implemented fine-tuning and evaluation using the high-level `Trainer` API with the latest features
-* Implemented a complete custom training loop from scratch with PyTorch
-* Used 🤗 Accelerate to make your training code work seamlessly on multiple GPUs or TPUs
-* Applied modern optimization techniques like mixed precision training and gradient accumulation
-
-> [!TIP]
-> 🎉 **Congratulations!** You've mastered the fundamentals of fine-tuning transformer models. You're now ready to tackle real-world ML projects!
->
-> 📖 **Continue Learning**: Explore these resources to deepen your knowledge:
-> - [🤗 Transformers task guides](https://huggingface.co/docs/transformers/main/en/tasks/sequence_classification) for specific NLP tasks
-> - [🤗 Transformers examples](https://huggingface.co/docs/transformers/main/en/notebooks) for comprehensive notebooks
->
-> 🚀 **Next Steps**:
-> - Try fine-tuning on your own dataset using the techniques you've learned
-> - Experiment with different model architectures available on the [Hugging Face Hub](https://huggingface.co/models)
-> - Join the [Hugging Face community](https://discuss.huggingface.co/) to share your projects and get help
-
-This is just the beginning of your journey with 🤗 Transformers. In the next chapter, we'll explore how to share your models and tokenizers with the community and contribute to the ever-growing ecosystem of pretrained models.
-
-The skills you've developed here - data preprocessing, training configuration, evaluation, and optimization - are fundamental to any machine learning project. Whether you're working on text classification, named entity recognition, question answering, or any other NLP task, these techniques will serve you well.
-
-> [!TIP]
-> 💡 **Pro Tips for Success**:
-> - Always start with a strong baseline using the `Trainer` API before implementing custom training loops
-> - Use the 🤗 Hub to find pretrained models that are close to your task for better starting points
-> - Monitor your training with proper evaluation metrics and don't forget to save checkpoints
-> - Leverage the community - share your models and datasets to help others and get feedback on your work
+
+
+# Fine-tuning, Check![[fine-tuning-check]]
+
+
+
+That was comprehensive! In the first two chapters you learned about models and tokenizers, and now you know how to fine-tune them for your own data using modern best practices. To recap, in this chapter you:
+
+* Learned about datasets on the [Hub](https://huggingface.co/datasets) and modern data processing techniques
+* Learned how to load and preprocess datasets efficiently, including using dynamic padding and data collators
+* Implemented fine-tuning and evaluation using the high-level `Trainer` API with the latest features
+* Implemented a complete custom training loop from scratch with PyTorch
+* Used 🤗 Accelerate to make your training code work seamlessly on multiple GPUs or TPUs
+* Applied modern optimization techniques like mixed precision training and gradient accumulation
+
+> [!TIP]
+> 🎉 **Congratulations!** You've mastered the fundamentals of fine-tuning transformer models. You're now ready to tackle real-world ML projects!
+>
+> 📖 **Continue Learning**: Explore these resources to deepen your knowledge:
+> - [🤗 Transformers task guides](https://huggingface.co/docs/transformers/main/en/tasks/sequence_classification) for specific NLP tasks
+> - [🤗 Transformers examples](https://huggingface.co/docs/transformers/main/en/notebooks) for comprehensive notebooks
+>
+> 🚀 **Next Steps**:
+> - Try fine-tuning on your own dataset using the techniques you've learned
+> - Experiment with different model architectures available on the [Hugging Face Hub](https://huggingface.co/models)
+> - Join the [Hugging Face community](https://discuss.huggingface.co/) to share your projects and get help
+
+This is just the beginning of your journey with 🤗 Transformers. In the next chapter, we'll explore how to share your models and tokenizers with the community and contribute to the ever-growing ecosystem of pretrained models.
+
+The skills you've developed here - data preprocessing, training configuration, evaluation, and optimization - are fundamental to any machine learning project. Whether you're working on text classification, named entity recognition, question answering, or any other NLP task, these techniques will serve you well.
+
+> [!TIP]
+> 💡 **Pro Tips for Success**:
+> - Always start with a strong baseline using the `Trainer` API before implementing custom training loops
+> - Use the 🤗 Hub to find pretrained models that are close to your task for better starting points
+> - Monitor your training with proper evaluation metrics and don't forget to save checkpoints
+> - Leverage the community - share your models and datasets to help others and get feedback on your work
diff --git a/chapters/en/chapter3/7.mdx b/chapters/en/chapter3/7.mdx
index ffb970e40..665f769d8 100644
--- a/chapters/en/chapter3/7.mdx
+++ b/chapters/en/chapter3/7.mdx
@@ -1,27 +1,27 @@
-
-
-# End-of-chapter Certificate
-
-
-
-Congratulations on completing the course! You've learned how to fine-tune pretrained models, understand learning curves, and share your models with the community. Now it's time to take the quiz to test your knowledge and get your certificate.
-
-To take the quiz, you will need to follow these steps:
-
-1. Sign in to your Hugging Face account.
-2. Answer the questions in the quiz.
-3. Submit your answers.
-
-
-## Multiple Choice Quiz
-
-In this quiz, you will be asked to select the correct answer from a list of options. We'll test you on the fundamentals of supervised finetuning.
-
-
+
+
+# End-of-chapter Certificate
+
+
+
+Congratulations on completing the course! You've learned how to fine-tune pretrained models, understand learning curves, and share your models with the community. Now it's time to take the quiz to test your knowledge and get your certificate.
+
+To take the quiz, you will need to follow these steps:
+
+1. Sign in to your Hugging Face account.
+2. Answer the questions in the quiz.
+3. Submit your answers.
+
+
+## Multiple Choice Quiz
+
+In this quiz, you will be asked to select the correct answer from a list of options. We'll test you on the fundamentals of supervised finetuning.
+
+
diff --git a/chapters/en/chapter4/1.mdx b/chapters/en/chapter4/1.mdx
index 783b32bf0..f31b30e5d 100644
--- a/chapters/en/chapter4/1.mdx
+++ b/chapters/en/chapter4/1.mdx
@@ -1,22 +1,22 @@
-# The Hugging Face Hub[[the-hugging-face-hub]]
-
-
-
-The [Hugging Face Hub](https://huggingface.co/) –- our main website –- is a central platform that enables anyone to discover, use, and contribute new state-of-the-art models and datasets. It hosts a wide variety of models, with more than 10,000 publicly available. We'll focus on the models in this chapter, and take a look at the datasets in Chapter 5.
-
-The models in the Hub are not limited to 🤗 Transformers or even NLP. There are models from [Flair](https://github.com/flairNLP/flair) and [AllenNLP](https://github.com/allenai/allennlp) for NLP, [Asteroid](https://github.com/asteroid-team/asteroid) and [pyannote](https://github.com/pyannote/pyannote-audio) for speech, and [timm](https://github.com/rwightman/pytorch-image-models) for vision, to name a few.
-
-Each of these models is hosted as a Git repository, which allows versioning and reproducibility. Sharing a model on the Hub means opening it up to the community and making it accessible to anyone looking to easily use it, in turn eliminating their need to train a model on their own and simplifying sharing and usage.
-
-Additionally, sharing a model on the Hub automatically deploys a hosted Inference API for that model. Anyone in the community is free to test it out directly on the model's page, with custom inputs and appropriate widgets.
-
-The best part is that sharing and using any public model on the Hub is completely free! [Paid plans](https://huggingface.co/pricing) also exist if you wish to share models privately.
-
-The video below shows how to navigate the Hub.
-
-
-
+# The Hugging Face Hub[[the-hugging-face-hub]]
+
+
+
+The [Hugging Face Hub](https://huggingface.co/) –- our main website –- is a central platform that enables anyone to discover, use, and contribute new state-of-the-art models and datasets. It hosts a wide variety of models, with more than 10,000 publicly available. We'll focus on the models in this chapter, and take a look at the datasets in Chapter 5.
+
+The models in the Hub are not limited to 🤗 Transformers or even NLP. There are models from [Flair](https://github.com/flairNLP/flair) and [AllenNLP](https://github.com/allenai/allennlp) for NLP, [Asteroid](https://github.com/asteroid-team/asteroid) and [pyannote](https://github.com/pyannote/pyannote-audio) for speech, and [timm](https://github.com/rwightman/pytorch-image-models) for vision, to name a few.
+
+Each of these models is hosted as a Git repository, which allows versioning and reproducibility. Sharing a model on the Hub means opening it up to the community and making it accessible to anyone looking to easily use it, in turn eliminating their need to train a model on their own and simplifying sharing and usage.
+
+Additionally, sharing a model on the Hub automatically deploys a hosted Inference API for that model. Anyone in the community is free to test it out directly on the model's page, with custom inputs and appropriate widgets.
+
+The best part is that sharing and using any public model on the Hub is completely free! [Paid plans](https://huggingface.co/pricing) also exist if you wish to share models privately.
+
+The video below shows how to navigate the Hub.
+
+
+
Having a huggingface.co account is required to follow along this part, as we'll be creating and managing repositories on the Hugging Face Hub: [create an account](https://huggingface.co/join)
\ No newline at end of file
diff --git a/chapters/en/chapter4/2.mdx b/chapters/en/chapter4/2.mdx
index e1fe5bebb..9d924aa53 100644
--- a/chapters/en/chapter4/2.mdx
+++ b/chapters/en/chapter4/2.mdx
@@ -1,95 +1,95 @@
-
-
-# Using pretrained models[[using-pretrained-models]]
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-The Model Hub makes selecting the appropriate model simple, so that using it in any downstream library can be done in a few lines of code. Let's take a look at how to actually use one of these models, and how to contribute back to the community.
-
-Let's say we're looking for a French-based model that can perform mask filling.
-
-
-

-
-
-We select the `camembert-base` checkpoint to try it out. The identifier `camembert-base` is all we need to start using it! As you've seen in previous chapters, we can instantiate it using the `pipeline()` function:
-
-```py
-from transformers import pipeline
-
-camembert_fill_mask = pipeline("fill-mask", model="camembert-base")
-results = camembert_fill_mask("Le camembert est :)")
-```
-
-```python out
-[
- {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'},
- {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'},
- {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'},
- {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'},
- {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'}
-]
-```
-
-As you can see, loading a model within a pipeline is extremely simple. The only thing you need to watch out for is that the chosen checkpoint is suitable for the task it's going to be used for. For example, here we are loading the `camembert-base` checkpoint in the `fill-mask` pipeline, which is completely fine. But if we were to load this checkpoint in the `text-classification` pipeline, the results would not make any sense because the head of `camembert-base` is not suitable for this task! We recommend using the task selector in the Hugging Face Hub interface in order to select the appropriate checkpoints:
-
-
-

-
-
-You can also instantiate the checkpoint using the model architecture directly:
-
-{#if fw === 'pt'}
-```py
-from transformers import CamembertTokenizer, CamembertForMaskedLM
-
-tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
-model = CamembertForMaskedLM.from_pretrained("camembert-base")
-```
-
-However, we recommend using the [`Auto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes) instead, as these are by design architecture-agnostic. While the previous code sample limits users to checkpoints loadable in the CamemBERT architecture, using the `Auto*` classes makes switching checkpoints simple:
-
-```py
-from transformers import AutoTokenizer, AutoModelForMaskedLM
-
-tokenizer = AutoTokenizer.from_pretrained("camembert-base")
-model = AutoModelForMaskedLM.from_pretrained("camembert-base")
-```
-{:else}
-```py
-from transformers import CamembertTokenizer, TFCamembertForMaskedLM
-
-tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
-model = TFCamembertForMaskedLM.from_pretrained("camembert-base")
-```
-
-However, we recommend using the [`TFAuto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes) instead, as these are by design architecture-agnostic. While the previous code sample limits users to checkpoints loadable in the CamemBERT architecture, using the `TFAuto*` classes makes switching checkpoints simple:
-
-```py
-from transformers import AutoTokenizer, TFAutoModelForMaskedLM
-
-tokenizer = AutoTokenizer.from_pretrained("camembert-base")
-model = TFAutoModelForMaskedLM.from_pretrained("camembert-base")
-```
-{/if}
-
-> [!TIP]
-> When using a pretrained model, make sure to check how it was trained, on which datasets, its limits, and its biases. All of this information should be indicated on its model card.
+
+
+# Using pretrained models[[using-pretrained-models]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+The Model Hub makes selecting the appropriate model simple, so that using it in any downstream library can be done in a few lines of code. Let's take a look at how to actually use one of these models, and how to contribute back to the community.
+
+Let's say we're looking for a French-based model that can perform mask filling.
+
+
+

+
+
+We select the `camembert-base` checkpoint to try it out. The identifier `camembert-base` is all we need to start using it! As you've seen in previous chapters, we can instantiate it using the `pipeline()` function:
+
+```py
+from transformers import pipeline
+
+camembert_fill_mask = pipeline("fill-mask", model="camembert-base")
+results = camembert_fill_mask("Le camembert est :)")
+```
+
+```python out
+[
+ {'sequence': 'Le camembert est délicieux :)', 'score': 0.49091005325317383, 'token': 7200, 'token_str': 'délicieux'},
+ {'sequence': 'Le camembert est excellent :)', 'score': 0.1055697426199913, 'token': 2183, 'token_str': 'excellent'},
+ {'sequence': 'Le camembert est succulent :)', 'score': 0.03453313186764717, 'token': 26202, 'token_str': 'succulent'},
+ {'sequence': 'Le camembert est meilleur :)', 'score': 0.0330314114689827, 'token': 528, 'token_str': 'meilleur'},
+ {'sequence': 'Le camembert est parfait :)', 'score': 0.03007650189101696, 'token': 1654, 'token_str': 'parfait'}
+]
+```
+
+As you can see, loading a model within a pipeline is extremely simple. The only thing you need to watch out for is that the chosen checkpoint is suitable for the task it's going to be used for. For example, here we are loading the `camembert-base` checkpoint in the `fill-mask` pipeline, which is completely fine. But if we were to load this checkpoint in the `text-classification` pipeline, the results would not make any sense because the head of `camembert-base` is not suitable for this task! We recommend using the task selector in the Hugging Face Hub interface in order to select the appropriate checkpoints:
+
+
+

+
+
+You can also instantiate the checkpoint using the model architecture directly:
+
+{#if fw === 'pt'}
+```py
+from transformers import CamembertTokenizer, CamembertForMaskedLM
+
+tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
+model = CamembertForMaskedLM.from_pretrained("camembert-base")
+```
+
+However, we recommend using the [`Auto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes) instead, as these are by design architecture-agnostic. While the previous code sample limits users to checkpoints loadable in the CamemBERT architecture, using the `Auto*` classes makes switching checkpoints simple:
+
+```py
+from transformers import AutoTokenizer, AutoModelForMaskedLM
+
+tokenizer = AutoTokenizer.from_pretrained("camembert-base")
+model = AutoModelForMaskedLM.from_pretrained("camembert-base")
+```
+{:else}
+```py
+from transformers import CamembertTokenizer, TFCamembertForMaskedLM
+
+tokenizer = CamembertTokenizer.from_pretrained("camembert-base")
+model = TFCamembertForMaskedLM.from_pretrained("camembert-base")
+```
+
+However, we recommend using the [`TFAuto*` classes](https://huggingface.co/transformers/model_doc/auto?highlight=auto#auto-classes) instead, as these are by design architecture-agnostic. While the previous code sample limits users to checkpoints loadable in the CamemBERT architecture, using the `TFAuto*` classes makes switching checkpoints simple:
+
+```py
+from transformers import AutoTokenizer, TFAutoModelForMaskedLM
+
+tokenizer = AutoTokenizer.from_pretrained("camembert-base")
+model = TFAutoModelForMaskedLM.from_pretrained("camembert-base")
+```
+{/if}
+
+> [!TIP]
+> When using a pretrained model, make sure to check how it was trained, on which datasets, its limits, and its biases. All of this information should be indicated on its model card.
diff --git a/chapters/en/chapter4/3.mdx b/chapters/en/chapter4/3.mdx
index 586cf03f0..faecb1045 100644
--- a/chapters/en/chapter4/3.mdx
+++ b/chapters/en/chapter4/3.mdx
@@ -1,637 +1,637 @@
-
-
-# Sharing pretrained models[[sharing-pretrained-models]]
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-In the steps below, we'll take a look at the easiest ways to share pretrained models to the 🤗 Hub. There are tools and utilities available that make it simple to share and update models directly on the Hub, which we will explore below.
-
-
-
-We encourage all users that train models to contribute by sharing them with the community — sharing models, even when trained on very specific datasets, will help others, saving them time and compute resources and providing access to useful trained artifacts. In turn, you can benefit from the work that others have done!
-
-There are three ways to go about creating new model repositories:
-
-- Using the `push_to_hub` API
-- Using the `huggingface_hub` Python library
-- Using the web interface
-
-Once you've created a repository, you can upload files to it via git and git-lfs. We'll walk you through creating model repositories and uploading files to them in the following sections.
-
-
-## Using the `push_to_hub` API[[using-the-pushtohub-api]]
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-The simplest way to upload files to the Hub is by leveraging the `push_to_hub` API.
-
-Before going further, you'll need to generate an authentication token so that the `huggingface_hub` API knows who you are and what namespaces you have write access to. Make sure you are in an environment where you have `transformers` installed (see [Setup](/course/chapter0)). If you are in a notebook, you can use the following function to login:
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-In a terminal, you can run:
-
-```bash
-huggingface-cli login
-```
-
-In both cases, you should be prompted for your username and password, which are the same ones you use to log in to the Hub. If you do not have a Hub profile yet, you should create one [here](https://huggingface.co/join).
-
-Great! You now have your authentication token stored in your cache folder. Let's create some repositories!
-
-{#if fw === 'pt'}
-
-If you have played around with the `Trainer` API to train a model, the easiest way to upload it to the Hub is to set `push_to_hub=True` when you define your `TrainingArguments`:
-
-```py
-from transformers import TrainingArguments
-
-training_args = TrainingArguments(
- "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True
-)
-```
-
-When you call `trainer.train()`, the `Trainer` will then upload your model to the Hub each time it is saved (here every epoch) in a repository in your namespace. That repository will be named like the output directory you picked (here `bert-finetuned-mrpc`) but you can choose a different name with `hub_model_id = "a_different_name"`.
-
-To upload your model to an organization you are a member of, just pass it with `hub_model_id = "my_organization/my_repo_name"`.
-
-Once your training is finished, you should do a final `trainer.push_to_hub()` to upload the last version of your model. It will also generate a model card with all the relevant metadata, reporting the hyperparameters used and the evaluation results! Here is an example of the content you might find in a such a model card:
-
-
-

-
-
-{:else}
-
-If you are using Keras to train your model, the easiest way to upload it to the Hub is to pass along a `PushToHubCallback` when you call `model.fit()`:
-
-```py
-from transformers import PushToHubCallback
-
-callback = PushToHubCallback(
- "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer
-)
-```
-
-Then you should add `callbacks=[callback]` in your call to `model.fit()`. The callback will then upload your model to the Hub each time it is saved (here every epoch) in a repository in your namespace. That repository will be named like the output directory you picked (here `bert-finetuned-mrpc`) but you can choose a different name with `hub_model_id = "a_different_name"`.
-
-To upload you model to an organization you are a member of, just pass it with `hub_model_id = "my_organization/my_repo_name"`.
-
-{/if}
-
-At a lower level, accessing the Model Hub can be done directly on models, tokenizers, and configuration objects via their `push_to_hub()` method. This method takes care of both the repository creation and pushing the model and tokenizer files directly to the repository. No manual handling is required, unlike with the API we'll see below.
-
-To get an idea of how it works, let's first initialize a model and a tokenizer:
-
-{#if fw === 'pt'}
-```py
-from transformers import AutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = AutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-```
-{:else}
-```py
-from transformers import TFAutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-```
-{/if}
-
-You're free to do whatever you want with these — add tokens to the tokenizer, train the model, fine-tune it. Once you're happy with the resulting model, weights, and tokenizer, you can leverage the `push_to_hub()` method directly available on the `model` object:
-
-```py
-model.push_to_hub("dummy-model")
-```
-
-This will create the new repository `dummy-model` in your profile, and populate it with your model files.
-Do the same with the tokenizer, so that all the files are now available in this repository:
-
-```py
-tokenizer.push_to_hub("dummy-model")
-```
-
-If you belong to an organization, simply specify the `organization` argument to upload to that organization's namespace:
-
-```py
-tokenizer.push_to_hub("dummy-model", organization="huggingface")
-```
-
-If you wish to use a specific Hugging Face token, you're free to specify it to the `push_to_hub()` method as well:
-
-```py
-tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="")
-```
-
-Now head to the Model Hub to find your newly uploaded model: *https://huggingface.co/user-or-organization/dummy-model*.
-
-Click on the "Files and versions" tab, and you should see the files visible in the following screenshot:
-
-{#if fw === 'pt'}
-
-

-
-{:else}
-
-

-
-{/if}
-
-> [!TIP]
-> ✏️ **Try it out!** Take the model and tokenizer associated with the `bert-base-cased` checkpoint and upload them to a repo in your namespace using the `push_to_hub()` method. Double-check that the repo appears properly on your page before deleting it.
-
-As you've seen, the `push_to_hub()` method accepts several arguments, making it possible to upload to a specific repository or organization namespace, or to use a different API token. We recommend you take a look at the method specification available directly in the [🤗 Transformers documentation](https://huggingface.co/transformers/model_sharing) to get an idea of what is possible.
-
-The `push_to_hub()` method is backed by the [`huggingface_hub`](https://github.com/huggingface/huggingface_hub) Python package, which offers a direct API to the Hugging Face Hub. It's integrated within 🤗 Transformers and several other machine learning libraries, like [`allenlp`](https://github.com/allenai/allennlp). Although we focus on the 🤗 Transformers integration in this chapter, integrating it into your own code or library is simple.
-
-Jump to the last section to see how to upload files to your newly created repository!
-
-## Using the `huggingface_hub` Python library[[using-the-huggingfacehub-python-library]]
-
-The `huggingface_hub` Python library is a package which offers a set of tools for the model and datasets hubs. It provides simple methods and classes for common tasks like
-getting information about repositories on the hub and managing them. It provides simple APIs that work on top of git to manage those repositories' content and to integrate the Hub
-in your projects and libraries.
-
-Similarly to using the `push_to_hub` API, this will require you to have your API token saved in your cache. In order to do this, you will need to use the `login` command from the CLI, as mentioned in the previous section (again, make sure to prepend these commands with the `!` character if running in Google Colab):
-
-```bash
-huggingface-cli login
-```
-
-The `huggingface_hub` package offers several methods and classes which are useful for our purpose. Firstly, there are a few methods to manage repository creation, deletion, and others:
-
-```python no-format
-from huggingface_hub import (
- # User management
- login,
- logout,
- whoami,
-
- # Repository creation and management
- create_repo,
- delete_repo,
- update_repo_visibility,
-
- # And some methods to retrieve/change information about the content
- list_models,
- list_datasets,
- list_metrics,
- list_repo_files,
- upload_file,
- delete_file,
-)
-```
-
-
-Additionally, it offers the very powerful `Repository` class to manage a local repository. We will explore these methods and that class in the next few section to understand how to leverage them.
-
-The `create_repo` method can be used to create a new repository on the hub:
-
-```py
-from huggingface_hub import create_repo
-
-create_repo("dummy-model")
-```
-
-This will create the repository `dummy-model` in your namespace. If you like, you can specify which organization the repository should belong to using the `organization` argument:
-
-```py
-from huggingface_hub import create_repo
-
-create_repo("dummy-model", organization="huggingface")
-```
-
-This will create the `dummy-model` repository in the `huggingface` namespace, assuming you belong to that organization.
-Other arguments which may be useful are:
-
-- `private`, in order to specify if the repository should be visible from others or not.
-- `token`, if you would like to override the token stored in your cache by a given token.
-- `repo_type`, if you would like to create a `dataset` or a `space` instead of a model. Accepted values are `"dataset"` and `"space"`.
-
-Once the repository is created, we should add files to it! Jump to the next section to see the three ways this can be handled.
-
-
-## Using the web interface[[using-the-web-interface]]
-
-The web interface offers tools to manage repositories directly in the Hub. Using the interface, you can easily create repositories, add files (even large ones!), explore models, visualize diffs, and much more.
-
-To create a new repository, visit [huggingface.co/new](https://huggingface.co/new):
-
-
-

-
-
-First, specify the owner of the repository: this can be either you or any of the organizations you're affiliated with. If you choose an organization, the model will be featured on the organization's page and every member of the organization will have the ability to contribute to the repository.
-
-Next, enter your model's name. This will also be the name of the repository. Finally, you can specify whether you want your model to be public or private. Private models are hidden from public view.
-
-After creating your model repository, you should see a page like this:
-
-
-

-
-
-This is where your model will be hosted. To start populating it, you can add a README file directly from the web interface.
-
-
-

-
-
-The README file is in Markdown — feel free to go wild with it! The third part of this chapter is dedicated to building a model card. These are of prime importance in bringing value to your model, as they're where you tell others what it can do.
-
-If you look at the "Files and versions" tab, you'll see that there aren't many files there yet — just the *README.md* you just created and the *.gitattributes* file that keeps track of large files.
-
-
-

-
-
-We'll take a look at how to add some new files next.
-
-## Uploading the model files[[uploading-the-model-files]]
-
-The system to manage files on the Hugging Face Hub is based on git for regular files, and git-lfs (which stands for [Git Large File Storage](https://git-lfs.github.com/)) for larger files.
-
-In the next section, we go over three different ways of uploading files to the Hub: through `huggingface_hub` and through git commands.
-
-### The `upload_file` approach[[the-uploadfile-approach]]
-
-Using `upload_file` does not require git and git-lfs to be installed on your system. It pushes files directly to the 🤗 Hub using HTTP POST requests. A limitation of this approach is that it doesn't handle files that are larger than 5GB in size.
-If your files are larger than 5GB, please follow the two other methods detailed below.
-
-The API may be used as follows:
-
-```py
-from huggingface_hub import upload_file
-
-upload_file(
- "/config.json",
- path_in_repo="config.json",
- repo_id="/dummy-model",
-)
-```
-
-This will upload the file `config.json` available at `` to the root of the repository as `config.json`, to the `dummy-model` repository.
-Other arguments which may be useful are:
-
-- `token`, if you would like to override the token stored in your cache by a given token.
-- `repo_type`, if you would like to upload to a `dataset` or a `space` instead of a model. Accepted values are `"dataset"` and `"space"`.
-
-
-### The `Repository` class[[the-repository-class]]
-
-The `Repository` class manages a local repository in a git-like manner. It abstracts most of the pain points one may have with git to provide all features that we require.
-
-Using this class requires having git and git-lfs installed, so make sure you have git-lfs installed (see [here](https://git-lfs.github.com/) for installation instructions) and set up before you begin.
-
-In order to start playing around with the repository we have just created, we can start by initialising it into a local folder by cloning the remote repository:
-
-```py
-from huggingface_hub import Repository
-
-repo = Repository("", clone_from="/dummy-model")
-```
-
-This created the folder `` in our working directory. This folder only contains the `.gitattributes` file as that's the only file created when instantiating the repository through `create_repo`.
-
-From this point on, we may leverage several of the traditional git methods:
-
-```py
-repo.git_pull()
-repo.git_add()
-repo.git_commit()
-repo.git_push()
-repo.git_tag()
-```
-
-And others! We recommend taking a look at the `Repository` documentation available [here](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management) for an overview of all available methods.
-
-At present, we have a model and a tokenizer that we would like to push to the hub. We have successfully cloned the repository, we can therefore save the files within that repository.
-
-We first make sure that our local clone is up to date by pulling the latest changes:
-
-```py
-repo.git_pull()
-```
-
-Once that is done, we save the model and tokenizer files:
-
-```py
-model.save_pretrained("")
-tokenizer.save_pretrained("")
-```
-
-The `` now contains all the model and tokenizer files. We follow the usual git workflow by adding files to the staging area, committing them and pushing them to the hub:
-
-```py
-repo.git_add()
-repo.git_commit("Add model and tokenizer files")
-repo.git_push()
-```
-
-Congratulations! You just pushed your first files on the hub.
-
-### The git-based approach[[the-git-based-approach]]
-
-This is the very barebones approach to uploading files: we'll do so with git and git-lfs directly. Most of the difficulty is abstracted away by previous approaches, but there are a few caveats with the following method so we'll follow a more complex use-case.
-
-Using this class requires having git and git-lfs installed, so make sure you have [git-lfs](https://git-lfs.github.com/) installed (see here for installation instructions) and set up before you begin.
-
-First start by initializing git-lfs:
-
-```bash
-git lfs install
-```
-
-```bash
-Updated git hooks.
-Git LFS initialized.
-```
-
-Once that's done, the first step is to clone your model repository:
-
-```bash
-git clone https://huggingface.co//
-```
-
-My username is `lysandre` and I've used the model name `dummy`, so for me the command ends up looking like the following:
-
-```
-git clone https://huggingface.co/lysandre/dummy
-```
-
-I now have a folder named *dummy* in my working directory. I can `cd` into the folder and have a look at the contents:
-
-```bash
-cd dummy && ls
-```
-
-```bash
-README.md
-```
-
-If you just created your repository using Hugging Face Hub's `create_repo` method, this folder should only contain a hidden `.gitattributes` file. If you followed the instructions in the previous section to create a repository using the web interface, the folder should contain a single *README.md* file alongside the hidden `.gitattributes` file, as shown here.
-
-Adding a regular-sized file, such as a configuration file, a vocabulary file, or basically any file under a few megabytes, is done exactly as one would do it in any git-based system. However, bigger files must be registered through git-lfs in order to push them to *huggingface.co*.
-
-Let's go back to Python for a bit to generate a model and tokenizer that we'd like to commit to our dummy repository:
-
-{#if fw === 'pt'}
-```py
-from transformers import AutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = AutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-# Do whatever with the model, train it, fine-tune it...
-
-model.save_pretrained("")
-tokenizer.save_pretrained("")
-```
-{:else}
-```py
-from transformers import TFAutoModelForMaskedLM, AutoTokenizer
-
-checkpoint = "camembert-base"
-
-model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
-tokenizer = AutoTokenizer.from_pretrained(checkpoint)
-
-# Do whatever with the model, train it, fine-tune it...
-
-model.save_pretrained("")
-tokenizer.save_pretrained("")
-```
-{/if}
-
-Now that we've saved some model and tokenizer artifacts, let's take another look at the *dummy* folder:
-
-```bash
-ls
-```
-
-{#if fw === 'pt'}
-```bash
-config.json pytorch_model.bin README.md sentencepiece.bpe.model special_tokens_map.json tokenizer_config.json tokenizer.json
-```
-
-If you look at the file sizes (for example, with `ls -lh`), you should see that the model state dict file (*pytorch_model.bin*) is the only outlier, at more than 400 MB.
-
-{:else}
-```bash
-config.json README.md sentencepiece.bpe.model special_tokens_map.json tf_model.h5 tokenizer_config.json tokenizer.json
-```
-
-If you look at the file sizes (for example, with `ls -lh`), you should see that the model state dict file (*t5_model.h5*) is the only outlier, at more than 400 MB.
-
-{/if}
-
-> [!TIP]
-> ✏️ When creating the repository from the web interface, the *.gitattributes* file is automatically set up to consider files with certain extensions, such as *.bin* and *.h5*, as large files, and git-lfs will track them with no necessary setup on your side.
-
-We can now go ahead and proceed like we would usually do with traditional Git repositories. We can add all the files to Git's staging environment using the `git add` command:
-
-```bash
-git add .
-```
-
-We can then have a look at the files that are currently staged:
-
-```bash
-git status
-```
-
-{#if fw === 'pt'}
-```bash
-On branch main
-Your branch is up to date with 'origin/main'.
-
-Changes to be committed:
- (use "git restore --staged ..." to unstage)
- modified: .gitattributes
- new file: config.json
- new file: pytorch_model.bin
- new file: sentencepiece.bpe.model
- new file: special_tokens_map.json
- new file: tokenizer.json
- new file: tokenizer_config.json
-```
-{:else}
-```bash
-On branch main
-Your branch is up to date with 'origin/main'.
-
-Changes to be committed:
- (use "git restore --staged ..." to unstage)
- modified: .gitattributes
- new file: config.json
- new file: sentencepiece.bpe.model
- new file: special_tokens_map.json
- new file: tf_model.h5
- new file: tokenizer.json
- new file: tokenizer_config.json
-```
-{/if}
-
-Similarly, we can make sure that git-lfs is tracking the correct files by using its `status` command:
-
-```bash
-git lfs status
-```
-
-{#if fw === 'pt'}
-```bash
-On branch main
-Objects to be pushed to origin/main:
-
-
-Objects to be committed:
-
- config.json (Git: bc20ff2)
- pytorch_model.bin (LFS: 35686c2)
- sentencepiece.bpe.model (LFS: 988bc5a)
- special_tokens_map.json (Git: cb23931)
- tokenizer.json (Git: 851ff3e)
- tokenizer_config.json (Git: f0f7783)
-
-Objects not staged for commit:
-
-
-```
-
-We can see that all files have `Git` as a handler, except *pytorch_model.bin* and *sentencepiece.bpe.model*, which have `LFS`. Great!
-
-{:else}
-```bash
-On branch main
-Objects to be pushed to origin/main:
-
-
-Objects to be committed:
-
- config.json (Git: bc20ff2)
- sentencepiece.bpe.model (LFS: 988bc5a)
- special_tokens_map.json (Git: cb23931)
- tf_model.h5 (LFS: 86fce29)
- tokenizer.json (Git: 851ff3e)
- tokenizer_config.json (Git: f0f7783)
-
-Objects not staged for commit:
-
-
-```
-
-We can see that all files have `Git` as a handler, except *t5_model.h5*, which has `LFS`. Great!
-
-{/if}
-
-Let's proceed to the final steps, committing and pushing to the *huggingface.co* remote repository:
-
-```bash
-git commit -m "First model version"
-```
-
-{#if fw === 'pt'}
-```bash
-[main b08aab1] First model version
- 7 files changed, 29027 insertions(+)
- 6 files changed, 36 insertions(+)
- create mode 100644 config.json
- create mode 100644 pytorch_model.bin
- create mode 100644 sentencepiece.bpe.model
- create mode 100644 special_tokens_map.json
- create mode 100644 tokenizer.json
- create mode 100644 tokenizer_config.json
-```
-{:else}
-```bash
-[main b08aab1] First model version
- 6 files changed, 36 insertions(+)
- create mode 100644 config.json
- create mode 100644 sentencepiece.bpe.model
- create mode 100644 special_tokens_map.json
- create mode 100644 tf_model.h5
- create mode 100644 tokenizer.json
- create mode 100644 tokenizer_config.json
-```
-{/if}
-
-Pushing can take a bit of time, depending on the speed of your internet connection and the size of your files:
-
-```bash
-git push
-```
-
-```bash
-Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done.
-Enumerating objects: 11, done.
-Counting objects: 100% (11/11), done.
-Delta compression using up to 12 threads
-Compressing objects: 100% (9/9), done.
-Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done.
-Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
-To https://huggingface.co/lysandre/dummy
- 891b41d..b08aab1 main -> main
-```
-
-{#if fw === 'pt'}
-If we take a look at the model repository when this is finished, we can see all the recently added files:
-
-
-

-
-
-The UI allows you to explore the model files and commits and to see the diff introduced by each commit:
-
-
-

-
-{:else}
-If we take a look at the model repository when this is finished, we can see all the recently added files:
-
-
-

-
-
-The UI allows you to explore the model files and commits and to see the diff introduced by each commit:
-
-
-

-
-{/if}
+
+
+# Sharing pretrained models[[sharing-pretrained-models]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+In the steps below, we'll take a look at the easiest ways to share pretrained models to the 🤗 Hub. There are tools and utilities available that make it simple to share and update models directly on the Hub, which we will explore below.
+
+
+
+We encourage all users that train models to contribute by sharing them with the community — sharing models, even when trained on very specific datasets, will help others, saving them time and compute resources and providing access to useful trained artifacts. In turn, you can benefit from the work that others have done!
+
+There are three ways to go about creating new model repositories:
+
+- Using the `push_to_hub` API
+- Using the `huggingface_hub` Python library
+- Using the web interface
+
+Once you've created a repository, you can upload files to it via git and git-lfs. We'll walk you through creating model repositories and uploading files to them in the following sections.
+
+
+## Using the `push_to_hub` API[[using-the-pushtohub-api]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+The simplest way to upload files to the Hub is by leveraging the `push_to_hub` API.
+
+Before going further, you'll need to generate an authentication token so that the `huggingface_hub` API knows who you are and what namespaces you have write access to. Make sure you are in an environment where you have `transformers` installed (see [Setup](/course/chapter0)). If you are in a notebook, you can use the following function to login:
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+In a terminal, you can run:
+
+```bash
+huggingface-cli login
+```
+
+In both cases, you should be prompted for your username and password, which are the same ones you use to log in to the Hub. If you do not have a Hub profile yet, you should create one [here](https://huggingface.co/join).
+
+Great! You now have your authentication token stored in your cache folder. Let's create some repositories!
+
+{#if fw === 'pt'}
+
+If you have played around with the `Trainer` API to train a model, the easiest way to upload it to the Hub is to set `push_to_hub=True` when you define your `TrainingArguments`:
+
+```py
+from transformers import TrainingArguments
+
+training_args = TrainingArguments(
+ "bert-finetuned-mrpc", save_strategy="epoch", push_to_hub=True
+)
+```
+
+When you call `trainer.train()`, the `Trainer` will then upload your model to the Hub each time it is saved (here every epoch) in a repository in your namespace. That repository will be named like the output directory you picked (here `bert-finetuned-mrpc`) but you can choose a different name with `hub_model_id = "a_different_name"`.
+
+To upload your model to an organization you are a member of, just pass it with `hub_model_id = "my_organization/my_repo_name"`.
+
+Once your training is finished, you should do a final `trainer.push_to_hub()` to upload the last version of your model. It will also generate a model card with all the relevant metadata, reporting the hyperparameters used and the evaluation results! Here is an example of the content you might find in a such a model card:
+
+
+

+
+
+{:else}
+
+If you are using Keras to train your model, the easiest way to upload it to the Hub is to pass along a `PushToHubCallback` when you call `model.fit()`:
+
+```py
+from transformers import PushToHubCallback
+
+callback = PushToHubCallback(
+ "bert-finetuned-mrpc", save_strategy="epoch", tokenizer=tokenizer
+)
+```
+
+Then you should add `callbacks=[callback]` in your call to `model.fit()`. The callback will then upload your model to the Hub each time it is saved (here every epoch) in a repository in your namespace. That repository will be named like the output directory you picked (here `bert-finetuned-mrpc`) but you can choose a different name with `hub_model_id = "a_different_name"`.
+
+To upload you model to an organization you are a member of, just pass it with `hub_model_id = "my_organization/my_repo_name"`.
+
+{/if}
+
+At a lower level, accessing the Model Hub can be done directly on models, tokenizers, and configuration objects via their `push_to_hub()` method. This method takes care of both the repository creation and pushing the model and tokenizer files directly to the repository. No manual handling is required, unlike with the API we'll see below.
+
+To get an idea of how it works, let's first initialize a model and a tokenizer:
+
+{#if fw === 'pt'}
+```py
+from transformers import AutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = AutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+{:else}
+```py
+from transformers import TFAutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+```
+{/if}
+
+You're free to do whatever you want with these — add tokens to the tokenizer, train the model, fine-tune it. Once you're happy with the resulting model, weights, and tokenizer, you can leverage the `push_to_hub()` method directly available on the `model` object:
+
+```py
+model.push_to_hub("dummy-model")
+```
+
+This will create the new repository `dummy-model` in your profile, and populate it with your model files.
+Do the same with the tokenizer, so that all the files are now available in this repository:
+
+```py
+tokenizer.push_to_hub("dummy-model")
+```
+
+If you belong to an organization, simply specify the `organization` argument to upload to that organization's namespace:
+
+```py
+tokenizer.push_to_hub("dummy-model", organization="huggingface")
+```
+
+If you wish to use a specific Hugging Face token, you're free to specify it to the `push_to_hub()` method as well:
+
+```py
+tokenizer.push_to_hub("dummy-model", organization="huggingface", use_auth_token="")
+```
+
+Now head to the Model Hub to find your newly uploaded model: *https://huggingface.co/user-or-organization/dummy-model*.
+
+Click on the "Files and versions" tab, and you should see the files visible in the following screenshot:
+
+{#if fw === 'pt'}
+
+

+
+{:else}
+
+

+
+{/if}
+
+> [!TIP]
+> ✏️ **Try it out!** Take the model and tokenizer associated with the `bert-base-cased` checkpoint and upload them to a repo in your namespace using the `push_to_hub()` method. Double-check that the repo appears properly on your page before deleting it.
+
+As you've seen, the `push_to_hub()` method accepts several arguments, making it possible to upload to a specific repository or organization namespace, or to use a different API token. We recommend you take a look at the method specification available directly in the [🤗 Transformers documentation](https://huggingface.co/transformers/model_sharing) to get an idea of what is possible.
+
+The `push_to_hub()` method is backed by the [`huggingface_hub`](https://github.com/huggingface/huggingface_hub) Python package, which offers a direct API to the Hugging Face Hub. It's integrated within 🤗 Transformers and several other machine learning libraries, like [`allenlp`](https://github.com/allenai/allennlp). Although we focus on the 🤗 Transformers integration in this chapter, integrating it into your own code or library is simple.
+
+Jump to the last section to see how to upload files to your newly created repository!
+
+## Using the `huggingface_hub` Python library[[using-the-huggingfacehub-python-library]]
+
+The `huggingface_hub` Python library is a package which offers a set of tools for the model and datasets hubs. It provides simple methods and classes for common tasks like
+getting information about repositories on the hub and managing them. It provides simple APIs that work on top of git to manage those repositories' content and to integrate the Hub
+in your projects and libraries.
+
+Similarly to using the `push_to_hub` API, this will require you to have your API token saved in your cache. In order to do this, you will need to use the `login` command from the CLI, as mentioned in the previous section (again, make sure to prepend these commands with the `!` character if running in Google Colab):
+
+```bash
+huggingface-cli login
+```
+
+The `huggingface_hub` package offers several methods and classes which are useful for our purpose. Firstly, there are a few methods to manage repository creation, deletion, and others:
+
+```python no-format
+from huggingface_hub import (
+ # User management
+ login,
+ logout,
+ whoami,
+
+ # Repository creation and management
+ create_repo,
+ delete_repo,
+ update_repo_visibility,
+
+ # And some methods to retrieve/change information about the content
+ list_models,
+ list_datasets,
+ list_metrics,
+ list_repo_files,
+ upload_file,
+ delete_file,
+)
+```
+
+
+Additionally, it offers the very powerful `Repository` class to manage a local repository. We will explore these methods and that class in the next few section to understand how to leverage them.
+
+The `create_repo` method can be used to create a new repository on the hub:
+
+```py
+from huggingface_hub import create_repo
+
+create_repo("dummy-model")
+```
+
+This will create the repository `dummy-model` in your namespace. If you like, you can specify which organization the repository should belong to using the `organization` argument:
+
+```py
+from huggingface_hub import create_repo
+
+create_repo("dummy-model", organization="huggingface")
+```
+
+This will create the `dummy-model` repository in the `huggingface` namespace, assuming you belong to that organization.
+Other arguments which may be useful are:
+
+- `private`, in order to specify if the repository should be visible from others or not.
+- `token`, if you would like to override the token stored in your cache by a given token.
+- `repo_type`, if you would like to create a `dataset` or a `space` instead of a model. Accepted values are `"dataset"` and `"space"`.
+
+Once the repository is created, we should add files to it! Jump to the next section to see the three ways this can be handled.
+
+
+## Using the web interface[[using-the-web-interface]]
+
+The web interface offers tools to manage repositories directly in the Hub. Using the interface, you can easily create repositories, add files (even large ones!), explore models, visualize diffs, and much more.
+
+To create a new repository, visit [huggingface.co/new](https://huggingface.co/new):
+
+
+

+
+
+First, specify the owner of the repository: this can be either you or any of the organizations you're affiliated with. If you choose an organization, the model will be featured on the organization's page and every member of the organization will have the ability to contribute to the repository.
+
+Next, enter your model's name. This will also be the name of the repository. Finally, you can specify whether you want your model to be public or private. Private models are hidden from public view.
+
+After creating your model repository, you should see a page like this:
+
+
+

+
+
+This is where your model will be hosted. To start populating it, you can add a README file directly from the web interface.
+
+
+

+
+
+The README file is in Markdown — feel free to go wild with it! The third part of this chapter is dedicated to building a model card. These are of prime importance in bringing value to your model, as they're where you tell others what it can do.
+
+If you look at the "Files and versions" tab, you'll see that there aren't many files there yet — just the *README.md* you just created and the *.gitattributes* file that keeps track of large files.
+
+
+

+
+
+We'll take a look at how to add some new files next.
+
+## Uploading the model files[[uploading-the-model-files]]
+
+The system to manage files on the Hugging Face Hub is based on git for regular files, and git-lfs (which stands for [Git Large File Storage](https://git-lfs.github.com/)) for larger files.
+
+In the next section, we go over three different ways of uploading files to the Hub: through `huggingface_hub` and through git commands.
+
+### The `upload_file` approach[[the-uploadfile-approach]]
+
+Using `upload_file` does not require git and git-lfs to be installed on your system. It pushes files directly to the 🤗 Hub using HTTP POST requests. A limitation of this approach is that it doesn't handle files that are larger than 5GB in size.
+If your files are larger than 5GB, please follow the two other methods detailed below.
+
+The API may be used as follows:
+
+```py
+from huggingface_hub import upload_file
+
+upload_file(
+ "/config.json",
+ path_in_repo="config.json",
+ repo_id="/dummy-model",
+)
+```
+
+This will upload the file `config.json` available at `` to the root of the repository as `config.json`, to the `dummy-model` repository.
+Other arguments which may be useful are:
+
+- `token`, if you would like to override the token stored in your cache by a given token.
+- `repo_type`, if you would like to upload to a `dataset` or a `space` instead of a model. Accepted values are `"dataset"` and `"space"`.
+
+
+### The `Repository` class[[the-repository-class]]
+
+The `Repository` class manages a local repository in a git-like manner. It abstracts most of the pain points one may have with git to provide all features that we require.
+
+Using this class requires having git and git-lfs installed, so make sure you have git-lfs installed (see [here](https://git-lfs.github.com/) for installation instructions) and set up before you begin.
+
+In order to start playing around with the repository we have just created, we can start by initialising it into a local folder by cloning the remote repository:
+
+```py
+from huggingface_hub import Repository
+
+repo = Repository("", clone_from="/dummy-model")
+```
+
+This created the folder `` in our working directory. This folder only contains the `.gitattributes` file as that's the only file created when instantiating the repository through `create_repo`.
+
+From this point on, we may leverage several of the traditional git methods:
+
+```py
+repo.git_pull()
+repo.git_add()
+repo.git_commit()
+repo.git_push()
+repo.git_tag()
+```
+
+And others! We recommend taking a look at the `Repository` documentation available [here](https://github.com/huggingface/huggingface_hub/tree/main/src/huggingface_hub#advanced-programmatic-repository-management) for an overview of all available methods.
+
+At present, we have a model and a tokenizer that we would like to push to the hub. We have successfully cloned the repository, we can therefore save the files within that repository.
+
+We first make sure that our local clone is up to date by pulling the latest changes:
+
+```py
+repo.git_pull()
+```
+
+Once that is done, we save the model and tokenizer files:
+
+```py
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+
+The `` now contains all the model and tokenizer files. We follow the usual git workflow by adding files to the staging area, committing them and pushing them to the hub:
+
+```py
+repo.git_add()
+repo.git_commit("Add model and tokenizer files")
+repo.git_push()
+```
+
+Congratulations! You just pushed your first files on the hub.
+
+### The git-based approach[[the-git-based-approach]]
+
+This is the very barebones approach to uploading files: we'll do so with git and git-lfs directly. Most of the difficulty is abstracted away by previous approaches, but there are a few caveats with the following method so we'll follow a more complex use-case.
+
+Using this class requires having git and git-lfs installed, so make sure you have [git-lfs](https://git-lfs.github.com/) installed (see here for installation instructions) and set up before you begin.
+
+First start by initializing git-lfs:
+
+```bash
+git lfs install
+```
+
+```bash
+Updated git hooks.
+Git LFS initialized.
+```
+
+Once that's done, the first step is to clone your model repository:
+
+```bash
+git clone https://huggingface.co//
+```
+
+My username is `lysandre` and I've used the model name `dummy`, so for me the command ends up looking like the following:
+
+```
+git clone https://huggingface.co/lysandre/dummy
+```
+
+I now have a folder named *dummy* in my working directory. I can `cd` into the folder and have a look at the contents:
+
+```bash
+cd dummy && ls
+```
+
+```bash
+README.md
+```
+
+If you just created your repository using Hugging Face Hub's `create_repo` method, this folder should only contain a hidden `.gitattributes` file. If you followed the instructions in the previous section to create a repository using the web interface, the folder should contain a single *README.md* file alongside the hidden `.gitattributes` file, as shown here.
+
+Adding a regular-sized file, such as a configuration file, a vocabulary file, or basically any file under a few megabytes, is done exactly as one would do it in any git-based system. However, bigger files must be registered through git-lfs in order to push them to *huggingface.co*.
+
+Let's go back to Python for a bit to generate a model and tokenizer that we'd like to commit to our dummy repository:
+
+{#if fw === 'pt'}
+```py
+from transformers import AutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = AutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+# Do whatever with the model, train it, fine-tune it...
+
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+{:else}
+```py
+from transformers import TFAutoModelForMaskedLM, AutoTokenizer
+
+checkpoint = "camembert-base"
+
+model = TFAutoModelForMaskedLM.from_pretrained(checkpoint)
+tokenizer = AutoTokenizer.from_pretrained(checkpoint)
+
+# Do whatever with the model, train it, fine-tune it...
+
+model.save_pretrained("")
+tokenizer.save_pretrained("")
+```
+{/if}
+
+Now that we've saved some model and tokenizer artifacts, let's take another look at the *dummy* folder:
+
+```bash
+ls
+```
+
+{#if fw === 'pt'}
+```bash
+config.json pytorch_model.bin README.md sentencepiece.bpe.model special_tokens_map.json tokenizer_config.json tokenizer.json
+```
+
+If you look at the file sizes (for example, with `ls -lh`), you should see that the model state dict file (*pytorch_model.bin*) is the only outlier, at more than 400 MB.
+
+{:else}
+```bash
+config.json README.md sentencepiece.bpe.model special_tokens_map.json tf_model.h5 tokenizer_config.json tokenizer.json
+```
+
+If you look at the file sizes (for example, with `ls -lh`), you should see that the model state dict file (*t5_model.h5*) is the only outlier, at more than 400 MB.
+
+{/if}
+
+> [!TIP]
+> ✏️ When creating the repository from the web interface, the *.gitattributes* file is automatically set up to consider files with certain extensions, such as *.bin* and *.h5*, as large files, and git-lfs will track them with no necessary setup on your side.
+
+We can now go ahead and proceed like we would usually do with traditional Git repositories. We can add all the files to Git's staging environment using the `git add` command:
+
+```bash
+git add .
+```
+
+We can then have a look at the files that are currently staged:
+
+```bash
+git status
+```
+
+{#if fw === 'pt'}
+```bash
+On branch main
+Your branch is up to date with 'origin/main'.
+
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: .gitattributes
+ new file: config.json
+ new file: pytorch_model.bin
+ new file: sentencepiece.bpe.model
+ new file: special_tokens_map.json
+ new file: tokenizer.json
+ new file: tokenizer_config.json
+```
+{:else}
+```bash
+On branch main
+Your branch is up to date with 'origin/main'.
+
+Changes to be committed:
+ (use "git restore --staged ..." to unstage)
+ modified: .gitattributes
+ new file: config.json
+ new file: sentencepiece.bpe.model
+ new file: special_tokens_map.json
+ new file: tf_model.h5
+ new file: tokenizer.json
+ new file: tokenizer_config.json
+```
+{/if}
+
+Similarly, we can make sure that git-lfs is tracking the correct files by using its `status` command:
+
+```bash
+git lfs status
+```
+
+{#if fw === 'pt'}
+```bash
+On branch main
+Objects to be pushed to origin/main:
+
+
+Objects to be committed:
+
+ config.json (Git: bc20ff2)
+ pytorch_model.bin (LFS: 35686c2)
+ sentencepiece.bpe.model (LFS: 988bc5a)
+ special_tokens_map.json (Git: cb23931)
+ tokenizer.json (Git: 851ff3e)
+ tokenizer_config.json (Git: f0f7783)
+
+Objects not staged for commit:
+
+
+```
+
+We can see that all files have `Git` as a handler, except *pytorch_model.bin* and *sentencepiece.bpe.model*, which have `LFS`. Great!
+
+{:else}
+```bash
+On branch main
+Objects to be pushed to origin/main:
+
+
+Objects to be committed:
+
+ config.json (Git: bc20ff2)
+ sentencepiece.bpe.model (LFS: 988bc5a)
+ special_tokens_map.json (Git: cb23931)
+ tf_model.h5 (LFS: 86fce29)
+ tokenizer.json (Git: 851ff3e)
+ tokenizer_config.json (Git: f0f7783)
+
+Objects not staged for commit:
+
+
+```
+
+We can see that all files have `Git` as a handler, except *t5_model.h5*, which has `LFS`. Great!
+
+{/if}
+
+Let's proceed to the final steps, committing and pushing to the *huggingface.co* remote repository:
+
+```bash
+git commit -m "First model version"
+```
+
+{#if fw === 'pt'}
+```bash
+[main b08aab1] First model version
+ 7 files changed, 29027 insertions(+)
+ 6 files changed, 36 insertions(+)
+ create mode 100644 config.json
+ create mode 100644 pytorch_model.bin
+ create mode 100644 sentencepiece.bpe.model
+ create mode 100644 special_tokens_map.json
+ create mode 100644 tokenizer.json
+ create mode 100644 tokenizer_config.json
+```
+{:else}
+```bash
+[main b08aab1] First model version
+ 6 files changed, 36 insertions(+)
+ create mode 100644 config.json
+ create mode 100644 sentencepiece.bpe.model
+ create mode 100644 special_tokens_map.json
+ create mode 100644 tf_model.h5
+ create mode 100644 tokenizer.json
+ create mode 100644 tokenizer_config.json
+```
+{/if}
+
+Pushing can take a bit of time, depending on the speed of your internet connection and the size of your files:
+
+```bash
+git push
+```
+
+```bash
+Uploading LFS objects: 100% (1/1), 433 MB | 1.3 MB/s, done.
+Enumerating objects: 11, done.
+Counting objects: 100% (11/11), done.
+Delta compression using up to 12 threads
+Compressing objects: 100% (9/9), done.
+Writing objects: 100% (9/9), 288.27 KiB | 6.27 MiB/s, done.
+Total 9 (delta 1), reused 0 (delta 0), pack-reused 0
+To https://huggingface.co/lysandre/dummy
+ 891b41d..b08aab1 main -> main
+```
+
+{#if fw === 'pt'}
+If we take a look at the model repository when this is finished, we can see all the recently added files:
+
+
+

+
+
+The UI allows you to explore the model files and commits and to see the diff introduced by each commit:
+
+
+

+
+{:else}
+If we take a look at the model repository when this is finished, we can see all the recently added files:
+
+
+

+
+
+The UI allows you to explore the model files and commits and to see the diff introduced by each commit:
+
+
+

+
+{/if}
diff --git a/chapters/en/chapter4/4.mdx b/chapters/en/chapter4/4.mdx
index 15a38ecbb..c43f90dcd 100644
--- a/chapters/en/chapter4/4.mdx
+++ b/chapters/en/chapter4/4.mdx
@@ -1,87 +1,87 @@
-# Building a model card[[building-a-model-card]]
-
-
-
-The model card is a file which is arguably as important as the model and tokenizer files in a model repository. It is the central definition of the model, ensuring reusability by fellow community members and reproducibility of results, and providing a platform on which other members may build their artifacts.
-
-Documenting the training and evaluation process helps others understand what to expect of a model — and providing sufficient information regarding the data that was used and the preprocessing and postprocessing that were done ensures that the limitations, biases, and contexts in which the model is and is not useful can be identified and understood.
-
-Therefore, creating a model card that clearly defines your model is a very important step. Here, we provide some tips that will help you with this. Creating the model card is done through the *README.md* file you saw earlier, which is a Markdown file.
-
-The "model card" concept originates from a research direction from Google, first shared in the paper ["Model Cards for Model Reporting"](https://arxiv.org/abs/1810.03993) by Margaret Mitchell et al. A lot of information contained here is based on that paper, and we recommend you take a look at it to understand why model cards are so important in a world that values reproducibility, reusability, and fairness.
-
-The model card usually starts with a very brief, high-level overview of what the model is for, followed by additional details in the following sections:
-
-- Model description
-- Intended uses & limitations
-- How to use
-- Limitations and bias
-- Training data
-- Training procedure
-- Evaluation results
-
-Let's take a look at what each of these sections should contain.
-
-### Model description[[model-description]]
-
-The model description provides basic details about the model. This includes the architecture, version, if it was introduced in a paper, if an original implementation is available, the author, and general information about the model. Any copyright should be attributed here. General information about training procedures, parameters, and important disclaimers can also be mentioned in this section.
-
-### Intended uses & limitations[[intended-uses-limitations]]
-
-Here you describe the use cases the model is intended for, including the languages, fields, and domains where it can be applied. This section of the model card can also document areas that are known to be out of scope for the model, or where it is likely to perform suboptimally.
-
-### How to use[[how-to-use]]
-
-This section should include some examples of how to use the model. This can showcase usage of the `pipeline()` function, usage of the model and tokenizer classes, and any other code you think might be helpful.
-
-### Training data[[training-data]]
-
-This part should indicate which dataset(s) the model was trained on. A brief description of the dataset(s) is also welcome.
-
-### Training procedure[[training-procedure]]
-
-In this section you should describe all the relevant aspects of training that are useful from a reproducibility perspective. This includes any preprocessing and postprocessing that were done on the data, as well as details such as the number of epochs the model was trained for, the batch size, the learning rate, and so on.
-
-### Variable and metrics[[variable-and-metrics]]
-
-Here you should describe the metrics you use for evaluation, and the different factors you are mesuring. Mentioning which metric(s) were used, on which dataset and which dataset split, makes it easy to compare you model's performance compared to that of other models. These should be informed by the previous sections, such as the intended users and use cases.
-
-### Evaluation results[[evaluation-results]]
-
-Finally, provide an indication of how well the model performs on the evaluation dataset. If the model uses a decision threshold, either provide the decision threshold used in the evaluation, or provide details on evaluation at different thresholds for the intended uses.
-
-## Example[[example]]
-
-Check out the following for a few examples of well-crafted model cards:
-
-- [`bert-base-cased`](https://huggingface.co/bert-base-cased)
-- [`gpt2`](https://huggingface.co/gpt2)
-- [`distilbert`](https://huggingface.co/distilbert-base-uncased)
-
-More examples from different organizations and companies are available [here](https://github.com/huggingface/model_card/blob/master/examples.md).
-
-## Note[[note]]
-
-Model cards are not a requirement when publishing models, and you don't need to include all of the sections described above when you make one. However, explicit documentation of the model can only benefit future users, so we recommend that you fill in as many of the sections as possible to the best of your knowledge and ability.
-
-## Model card metadata[[model-card-metadata]]
-
-If you have done a little exploring of the Hugging Face Hub, you should have seen that some models belong to certain categories: you can filter them by tasks, languages, libraries, and more. The categories a model belongs to are identified according to the metadata you add in the model card header.
-
-For example, if you take a look at the [`camembert-base` model card](https://huggingface.co/camembert-base/blob/main/README.md), you should see the following lines in the model card header:
-
-```
----
-language: fr
-license: mit
-datasets:
-- oscar
----
-```
-
-This metadata is parsed by the Hugging Face Hub, which then identifies this model as being a French model, with an MIT license, trained on the Oscar dataset.
-
-The [full model card specification](https://github.com/huggingface/hub-docs/blame/main/modelcard.md) allows specifying languages, licenses, tags, datasets, metrics, as well as the evaluation results the model obtained when training.
+# Building a model card[[building-a-model-card]]
+
+
+
+The model card is a file which is arguably as important as the model and tokenizer files in a model repository. It is the central definition of the model, ensuring reusability by fellow community members and reproducibility of results, and providing a platform on which other members may build their artifacts.
+
+Documenting the training and evaluation process helps others understand what to expect of a model — and providing sufficient information regarding the data that was used and the preprocessing and postprocessing that were done ensures that the limitations, biases, and contexts in which the model is and is not useful can be identified and understood.
+
+Therefore, creating a model card that clearly defines your model is a very important step. Here, we provide some tips that will help you with this. Creating the model card is done through the *README.md* file you saw earlier, which is a Markdown file.
+
+The "model card" concept originates from a research direction from Google, first shared in the paper ["Model Cards for Model Reporting"](https://arxiv.org/abs/1810.03993) by Margaret Mitchell et al. A lot of information contained here is based on that paper, and we recommend you take a look at it to understand why model cards are so important in a world that values reproducibility, reusability, and fairness.
+
+The model card usually starts with a very brief, high-level overview of what the model is for, followed by additional details in the following sections:
+
+- Model description
+- Intended uses & limitations
+- How to use
+- Limitations and bias
+- Training data
+- Training procedure
+- Evaluation results
+
+Let's take a look at what each of these sections should contain.
+
+### Model description[[model-description]]
+
+The model description provides basic details about the model. This includes the architecture, version, if it was introduced in a paper, if an original implementation is available, the author, and general information about the model. Any copyright should be attributed here. General information about training procedures, parameters, and important disclaimers can also be mentioned in this section.
+
+### Intended uses & limitations[[intended-uses-limitations]]
+
+Here you describe the use cases the model is intended for, including the languages, fields, and domains where it can be applied. This section of the model card can also document areas that are known to be out of scope for the model, or where it is likely to perform suboptimally.
+
+### How to use[[how-to-use]]
+
+This section should include some examples of how to use the model. This can showcase usage of the `pipeline()` function, usage of the model and tokenizer classes, and any other code you think might be helpful.
+
+### Training data[[training-data]]
+
+This part should indicate which dataset(s) the model was trained on. A brief description of the dataset(s) is also welcome.
+
+### Training procedure[[training-procedure]]
+
+In this section you should describe all the relevant aspects of training that are useful from a reproducibility perspective. This includes any preprocessing and postprocessing that were done on the data, as well as details such as the number of epochs the model was trained for, the batch size, the learning rate, and so on.
+
+### Variable and metrics[[variable-and-metrics]]
+
+Here you should describe the metrics you use for evaluation, and the different factors you are mesuring. Mentioning which metric(s) were used, on which dataset and which dataset split, makes it easy to compare you model's performance compared to that of other models. These should be informed by the previous sections, such as the intended users and use cases.
+
+### Evaluation results[[evaluation-results]]
+
+Finally, provide an indication of how well the model performs on the evaluation dataset. If the model uses a decision threshold, either provide the decision threshold used in the evaluation, or provide details on evaluation at different thresholds for the intended uses.
+
+## Example[[example]]
+
+Check out the following for a few examples of well-crafted model cards:
+
+- [`bert-base-cased`](https://huggingface.co/bert-base-cased)
+- [`gpt2`](https://huggingface.co/gpt2)
+- [`distilbert`](https://huggingface.co/distilbert-base-uncased)
+
+More examples from different organizations and companies are available [here](https://github.com/huggingface/model_card/blob/master/examples.md).
+
+## Note[[note]]
+
+Model cards are not a requirement when publishing models, and you don't need to include all of the sections described above when you make one. However, explicit documentation of the model can only benefit future users, so we recommend that you fill in as many of the sections as possible to the best of your knowledge and ability.
+
+## Model card metadata[[model-card-metadata]]
+
+If you have done a little exploring of the Hugging Face Hub, you should have seen that some models belong to certain categories: you can filter them by tasks, languages, libraries, and more. The categories a model belongs to are identified according to the metadata you add in the model card header.
+
+For example, if you take a look at the [`camembert-base` model card](https://huggingface.co/camembert-base/blob/main/README.md), you should see the following lines in the model card header:
+
+```
+---
+language: fr
+license: mit
+datasets:
+- oscar
+---
+```
+
+This metadata is parsed by the Hugging Face Hub, which then identifies this model as being a French model, with an MIT license, trained on the Oscar dataset.
+
+The [full model card specification](https://github.com/huggingface/hub-docs/blame/main/modelcard.md) allows specifying languages, licenses, tags, datasets, metrics, as well as the evaluation results the model obtained when training.
diff --git a/chapters/en/chapter4/5.mdx b/chapters/en/chapter4/5.mdx
index ca8682716..fe985c199 100644
--- a/chapters/en/chapter4/5.mdx
+++ b/chapters/en/chapter4/5.mdx
@@ -1,12 +1,12 @@
-# Part 1 completed![[part-1-completed]]
-
-
-
-This is the end of the first part of the course! Part 2 will be released on November 15th with a big community event, see more information [here](https://huggingface.co/blog/course-launch-event).
-
-You should now be able to fine-tune a pretrained model on a text classification problem (single or pairs of sentences) and upload the result to the Model Hub. To make sure you mastered this first section, you should do exactly that on a problem that interests you (and not necessarily in English if you speak another language)! You can find help in the [Hugging Face forums](https://discuss.huggingface.co/) and share your project in [this topic](https://discuss.huggingface.co/t/share-your-projects/6803) once you're finished.
-
-We can't wait to see what you will build with this!
+# Part 1 completed![[part-1-completed]]
+
+
+
+This is the end of the first part of the course! Part 2 will be released on November 15th with a big community event, see more information [here](https://huggingface.co/blog/course-launch-event).
+
+You should now be able to fine-tune a pretrained model on a text classification problem (single or pairs of sentences) and upload the result to the Model Hub. To make sure you mastered this first section, you should do exactly that on a problem that interests you (and not necessarily in English if you speak another language)! You can find help in the [Hugging Face forums](https://discuss.huggingface.co/) and share your project in [this topic](https://discuss.huggingface.co/t/share-your-projects/6803) once you're finished.
+
+We can't wait to see what you will build with this!
diff --git a/chapters/en/chapter4/6.mdx b/chapters/en/chapter4/6.mdx
index f52d064eb..58d752504 100644
--- a/chapters/en/chapter4/6.mdx
+++ b/chapters/en/chapter4/6.mdx
@@ -1,228 +1,228 @@
-
-
-
-
-# End-of-chapter quiz[[end-of-chapter-quiz]]
-
-
-
-Let's test what you learned in this chapter!
-
-### 1. What are models on the Hub limited to?
-
-
-
-### 2. How can you manage models on the Hub?
-
-git-lfs for large files.",
- correct: true
- }
- ]}
-/>
-
-### 3. What can you do using the Hugging Face Hub web interface?
-
-
-
-### 4. What is a model card?
-
-
-
-### 5. Which of these objects of the 🤗 Transformers library can be directly shared on the Hub with `push_to_hub()`?
-
-{#if fw === 'pt'}
-push_to_hub method, and using it will push all the tokenizer files (vocabulary, architecture of the tokenizer, etc.) to a given repo. That's not the only right answer, though!",
- correct: true
- },
- {
- text: "A model configuration",
- explain: "Right! All model configurations have the push_to_hub method, and using it will push them to a given repo. What else can you share?",
- correct: true
- },
- {
- text: "A model",
- explain: "Correct! All models have the push_to_hub method, and using it will push them and their configuration files to a given repo. That's not all you can share, though.",
- correct: true
- },
- {
- text: "A Trainer",
- explain: "That's right — the Trainer also implements the push_to_hub method, and using it will upload the model, its configuration, the tokenizer, and a model card draft to a given repo. Try another answer!",
- correct: true
- }
- ]}
-/>
-{:else}
-push_to_hub method, and using it will push all the tokenizer files (vocabulary, architecture of the tokenizer, etc.) to a given repo. That's not the only right answer, though!",
- correct: true
- },
- {
- text: "A model configuration",
- explain: "Right! All model configurations have the push_to_hub method, and using it will push them to a given repo. What else can you share?",
- correct: true
- },
- {
- text: "A model",
- explain: "Correct! All models have the push_to_hub method, and using it will push them and their configuration files to a given repo. That's not all you can share, though.",
- correct: true
- },
- {
- text: "All of the above with a dedicated callback",
- explain: "That's right — the PushToHubCallback will regularly send all of those objects to a repo during training.",
- correct: true
- }
- ]}
-/>
-{/if}
-
-### 6. What is the first step when using the `push_to_hub()` method or the CLI tools?
-
-
-
-### 7. You're using a model and a tokenizer — how can you upload them to the Hub?
-
-huggingface_hub utility.",
- explain: "Models and tokenizers already benefit from huggingface_hub utilities: no need for additional wrapping!"
- },
- {
- text: "By saving them to disk and calling transformers-cli upload-model",
- explain: "The command upload-model does not exist."
- }
- ]}
-/>
-
-### 8. Which git operations can you do with the `Repository` class?
-
-git_commit() method is there for that.",
- correct: true
- },
- {
- text: "A pull",
- explain: "That is the purpose of the git_pull() method.",
- correct: true
- },
- {
- text: "A push",
- explain: "The method git_push() does this.",
- correct: true
- },
- {
- text: "A merge",
- explain: "No, that operation will never be possible with this API."
- }
- ]}
-/>
+
+
+
+
+# End-of-chapter quiz[[end-of-chapter-quiz]]
+
+
+
+Let's test what you learned in this chapter!
+
+### 1. What are models on the Hub limited to?
+
+
+
+### 2. How can you manage models on the Hub?
+
+git-lfs for large files.",
+ correct: true
+ }
+ ]}
+/>
+
+### 3. What can you do using the Hugging Face Hub web interface?
+
+
+
+### 4. What is a model card?
+
+
+
+### 5. Which of these objects of the 🤗 Transformers library can be directly shared on the Hub with `push_to_hub()`?
+
+{#if fw === 'pt'}
+push_to_hub method, and using it will push all the tokenizer files (vocabulary, architecture of the tokenizer, etc.) to a given repo. That's not the only right answer, though!",
+ correct: true
+ },
+ {
+ text: "A model configuration",
+ explain: "Right! All model configurations have the push_to_hub method, and using it will push them to a given repo. What else can you share?",
+ correct: true
+ },
+ {
+ text: "A model",
+ explain: "Correct! All models have the push_to_hub method, and using it will push them and their configuration files to a given repo. That's not all you can share, though.",
+ correct: true
+ },
+ {
+ text: "A Trainer",
+ explain: "That's right — the Trainer also implements the push_to_hub method, and using it will upload the model, its configuration, the tokenizer, and a model card draft to a given repo. Try another answer!",
+ correct: true
+ }
+ ]}
+/>
+{:else}
+push_to_hub method, and using it will push all the tokenizer files (vocabulary, architecture of the tokenizer, etc.) to a given repo. That's not the only right answer, though!",
+ correct: true
+ },
+ {
+ text: "A model configuration",
+ explain: "Right! All model configurations have the push_to_hub method, and using it will push them to a given repo. What else can you share?",
+ correct: true
+ },
+ {
+ text: "A model",
+ explain: "Correct! All models have the push_to_hub method, and using it will push them and their configuration files to a given repo. That's not all you can share, though.",
+ correct: true
+ },
+ {
+ text: "All of the above with a dedicated callback",
+ explain: "That's right — the PushToHubCallback will regularly send all of those objects to a repo during training.",
+ correct: true
+ }
+ ]}
+/>
+{/if}
+
+### 6. What is the first step when using the `push_to_hub()` method or the CLI tools?
+
+
+
+### 7. You're using a model and a tokenizer — how can you upload them to the Hub?
+
+huggingface_hub utility.",
+ explain: "Models and tokenizers already benefit from huggingface_hub utilities: no need for additional wrapping!"
+ },
+ {
+ text: "By saving them to disk and calling transformers-cli upload-model",
+ explain: "The command upload-model does not exist."
+ }
+ ]}
+/>
+
+### 8. Which git operations can you do with the `Repository` class?
+
+git_commit() method is there for that.",
+ correct: true
+ },
+ {
+ text: "A pull",
+ explain: "That is the purpose of the git_pull() method.",
+ correct: true
+ },
+ {
+ text: "A push",
+ explain: "The method git_push() does this.",
+ correct: true
+ },
+ {
+ text: "A merge",
+ explain: "No, that operation will never be possible with this API."
+ }
+ ]}
+/>
diff --git a/chapters/en/chapter5/1.mdx b/chapters/en/chapter5/1.mdx
index 4a1751fa6..f2f219bd7 100644
--- a/chapters/en/chapter5/1.mdx
+++ b/chapters/en/chapter5/1.mdx
@@ -1,22 +1,22 @@
-# Introduction[[introduction]]
-
-
-
-In [Chapter 3](/course/chapter3) you got your first taste of the 🤗 Datasets library and saw that there were three main steps when it came to fine-tuning a model:
-
-1. Load a dataset from the Hugging Face Hub.
-2. Preprocess the data with `Dataset.map()`.
-3. Load and compute metrics.
-
-But this is just scratching the surface of what 🤗 Datasets can do! In this chapter, we will take a deep dive into the library. Along the way, we'll find answers to the following questions:
-
-* What do you do when your dataset is not on the Hub?
-* How can you slice and dice a dataset? (And what if you _really_ need to use Pandas?)
-* What do you do when your dataset is huge and will melt your laptop's RAM?
-* What the heck are "memory mapping" and Apache Arrow?
-* How can you create your own dataset and push it to the Hub?
-
+# Introduction[[introduction]]
+
+
+
+In [Chapter 3](/course/chapter3) you got your first taste of the 🤗 Datasets library and saw that there were three main steps when it came to fine-tuning a model:
+
+1. Load a dataset from the Hugging Face Hub.
+2. Preprocess the data with `Dataset.map()`.
+3. Load and compute metrics.
+
+But this is just scratching the surface of what 🤗 Datasets can do! In this chapter, we will take a deep dive into the library. Along the way, we'll find answers to the following questions:
+
+* What do you do when your dataset is not on the Hub?
+* How can you slice and dice a dataset? (And what if you _really_ need to use Pandas?)
+* What do you do when your dataset is huge and will melt your laptop's RAM?
+* What the heck are "memory mapping" and Apache Arrow?
+* How can you create your own dataset and push it to the Hub?
+
The techniques you learn here will prepare you for the advanced tokenization and fine-tuning tasks in [Chapter 6](/course/chapter6) and [Chapter 7](/course/chapter7) -- so grab a coffee and let's get started!
\ No newline at end of file
diff --git a/chapters/en/chapter5/2.mdx b/chapters/en/chapter5/2.mdx
index ba7471279..80a25870d 100644
--- a/chapters/en/chapter5/2.mdx
+++ b/chapters/en/chapter5/2.mdx
@@ -1,158 +1,158 @@
-# What if my dataset isn't on the Hub?[[what-if-my-dataset-isnt-on-the-hub]]
-
-
-
-You know how to use the [Hugging Face Hub](https://huggingface.co/datasets) to download datasets, but you'll often find yourself working with data that is stored either on your laptop or on a remote server. In this section we'll show you how 🤗 Datasets can be used to load datasets that aren't available on the Hugging Face Hub.
-
-
-
-## Working with local and remote datasets[[working-with-local-and-remote-datasets]]
-
-🤗 Datasets provides loading scripts to handle the loading of local and remote datasets. It supports several common data formats, such as:
-
-| Data format | Loading script | Example |
-| :----------------: | :------------: | :-----------------------------------------------------: |
-| CSV & TSV | `csv` | `load_dataset("csv", data_files="my_file.csv")` |
-| Text files | `text` | `load_dataset("text", data_files="my_file.txt")` |
-| JSON & JSON Lines | `json` | `load_dataset("json", data_files="my_file.jsonl")` |
-| Pickled DataFrames | `pandas` | `load_dataset("pandas", data_files="my_dataframe.pkl")` |
-
-As shown in the table, for each data format we just need to specify the type of loading script in the `load_dataset()` function, along with a `data_files` argument that specifies the path to one or more files. Let's start by loading a dataset from local files; later we'll see how to do the same with remote files.
-
-## Loading a local dataset[[loading-a-local-dataset]]
-
-For this example we'll use the [SQuAD-it dataset](https://github.com/crux82/squad-it/), which is a large-scale dataset for question answering in Italian.
-
-The training and test splits are hosted on GitHub, so we can download them with a simple `wget` command:
-
-```python
-!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz
-!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz
-```
-
-This will download two compressed files called *SQuAD_it-train.json.gz* and *SQuAD_it-test.json.gz*, which we can decompress with the Linux `gzip` command:
-
-```python
-!gzip -dkv SQuAD_it-*.json.gz
-```
-
-```bash
-SQuAD_it-test.json.gz: 87.4% -- replaced with SQuAD_it-test.json
-SQuAD_it-train.json.gz: 82.2% -- replaced with SQuAD_it-train.json
-```
-
-We can see that the compressed files have been replaced with _SQuAD_it-train.json_ and _SQuAD_it-test.json_, and that the data is stored in the JSON format.
-
-> [!TIP]
-> ✎ If you're wondering why there's a `!` character in the above shell commands, that's because we're running them within a Jupyter notebook. Simply remove the prefix if you want to download and unzip the dataset within a terminal.
-
-To load a JSON file with the `load_dataset()` function, we just need to know if we're dealing with ordinary JSON (similar to a nested dictionary) or JSON Lines (line-separated JSON). Like many question answering datasets, SQuAD-it uses the nested format, with all the text stored in a `data` field. This means we can load the dataset by specifying the `field` argument as follows:
-
-```py
-from datasets import load_dataset
-
-squad_it_dataset = load_dataset("json", data_files="SQuAD_it-train.json", field="data")
-```
-
-By default, loading local files creates a `DatasetDict` object with a `train` split. We can see this by inspecting the `squad_it_dataset` object:
-
-```py
-squad_it_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['title', 'paragraphs'],
- num_rows: 442
- })
-})
-```
-
-This shows us the number of rows and the column names associated with the training set. We can view one of the examples by indexing into the `train` split as follows:
-
-```py
-squad_it_dataset["train"][0]
-```
-
-```python out
-{
- "title": "Terremoto del Sichuan del 2008",
- "paragraphs": [
- {
- "context": "Il terremoto del Sichuan del 2008 o il terremoto...",
- "qas": [
- {
- "answers": [{"answer_start": 29, "text": "2008"}],
- "id": "56cdca7862d2951400fa6826",
- "question": "In quale anno si è verificato il terremoto nel Sichuan?",
- },
- ...
- ],
- },
- ...
- ],
-}
-```
-
-Great, we've loaded our first local dataset! But while this worked for the training set, what we really want is to include both the `train` and `test` splits in a single `DatasetDict` object so we can apply `Dataset.map()` functions across both splits at once. To do this, we can provide a dictionary to the `data_files` argument that maps each split name to a file associated with that split:
-
-```py
-data_files = {"train": "SQuAD_it-train.json", "test": "SQuAD_it-test.json"}
-squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
-squad_it_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['title', 'paragraphs'],
- num_rows: 442
- })
- test: Dataset({
- features: ['title', 'paragraphs'],
- num_rows: 48
- })
-})
-```
-
-This is exactly what we wanted. Now, we can apply various preprocessing techniques to clean up the data, tokenize the reviews, and so on.
-
-> [!TIP]
-> The `data_files` argument of the `load_dataset()` function is quite flexible and can be either a single file path, a list of file paths, or a dictionary that maps split names to file paths. You can also glob files that match a specified pattern according to the rules used by the Unix shell (e.g., you can glob all the JSON files in a directory as a single split by setting `data_files="*.json"`). See the 🤗 Datasets [documentation](https://huggingface.co/docs/datasets/loading#local-and-remote-files) for more details.
-
-The loading scripts in 🤗 Datasets actually support automatic decompression of the input files, so we could have skipped the use of `gzip` by pointing the `data_files` argument directly to the compressed files:
-
-```py
-data_files = {"train": "SQuAD_it-train.json.gz", "test": "SQuAD_it-test.json.gz"}
-squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
-```
-
-This can be useful if you don't want to manually decompress many GZIP files. The automatic decompression also applies to other common formats like ZIP and TAR, so you just need to point `data_files` to the compressed files and you're good to go!
-
-Now that you know how to load local files on your laptop or desktop, let's take a look at loading remote files.
-
-## Loading a remote dataset[[loading-a-remote-dataset]]
-
-If you're working as a data scientist or coder in a company, there's a good chance the datasets you want to analyze are stored on some remote server. Fortunately, loading remote files is just as simple as loading local ones! Instead of providing a path to local files, we point the `data_files` argument of `load_dataset()` to one or more URLs where the remote files are stored. For example, for the SQuAD-it dataset hosted on GitHub, we can just point `data_files` to the _SQuAD_it-*.json.gz_ URLs as follows:
-
-```py
-url = "https://github.com/crux82/squad-it/raw/master/"
-data_files = {
- "train": url + "SQuAD_it-train.json.gz",
- "test": url + "SQuAD_it-test.json.gz",
-}
-squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
-```
-
-This returns the same `DatasetDict` object obtained above, but saves us the step of manually downloading and decompressing the _SQuAD_it-*.json.gz_ files. This wraps up our foray into the various ways to load datasets that aren't hosted on the Hugging Face Hub. Now that we've got a dataset to play with, let's get our hands dirty with various data-wrangling techniques!
-
-> [!TIP]
-> ✏️ **Try it out!** Pick another dataset hosted on GitHub or the [UCI Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php) and try loading it both locally and remotely using the techniques introduced above. For bonus points, try loading a dataset that’s stored in a CSV or text format (see the [documentation](https://huggingface.co/docs/datasets/loading#local-and-remote-files) for more information on these formats).
-
-
+# What if my dataset isn't on the Hub?[[what-if-my-dataset-isnt-on-the-hub]]
+
+
+
+You know how to use the [Hugging Face Hub](https://huggingface.co/datasets) to download datasets, but you'll often find yourself working with data that is stored either on your laptop or on a remote server. In this section we'll show you how 🤗 Datasets can be used to load datasets that aren't available on the Hugging Face Hub.
+
+
+
+## Working with local and remote datasets[[working-with-local-and-remote-datasets]]
+
+🤗 Datasets provides loading scripts to handle the loading of local and remote datasets. It supports several common data formats, such as:
+
+| Data format | Loading script | Example |
+| :----------------: | :------------: | :-----------------------------------------------------: |
+| CSV & TSV | `csv` | `load_dataset("csv", data_files="my_file.csv")` |
+| Text files | `text` | `load_dataset("text", data_files="my_file.txt")` |
+| JSON & JSON Lines | `json` | `load_dataset("json", data_files="my_file.jsonl")` |
+| Pickled DataFrames | `pandas` | `load_dataset("pandas", data_files="my_dataframe.pkl")` |
+
+As shown in the table, for each data format we just need to specify the type of loading script in the `load_dataset()` function, along with a `data_files` argument that specifies the path to one or more files. Let's start by loading a dataset from local files; later we'll see how to do the same with remote files.
+
+## Loading a local dataset[[loading-a-local-dataset]]
+
+For this example we'll use the [SQuAD-it dataset](https://github.com/crux82/squad-it/), which is a large-scale dataset for question answering in Italian.
+
+The training and test splits are hosted on GitHub, so we can download them with a simple `wget` command:
+
+```python
+!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-train.json.gz
+!wget https://github.com/crux82/squad-it/raw/master/SQuAD_it-test.json.gz
+```
+
+This will download two compressed files called *SQuAD_it-train.json.gz* and *SQuAD_it-test.json.gz*, which we can decompress with the Linux `gzip` command:
+
+```python
+!gzip -dkv SQuAD_it-*.json.gz
+```
+
+```bash
+SQuAD_it-test.json.gz: 87.4% -- replaced with SQuAD_it-test.json
+SQuAD_it-train.json.gz: 82.2% -- replaced with SQuAD_it-train.json
+```
+
+We can see that the compressed files have been replaced with _SQuAD_it-train.json_ and _SQuAD_it-test.json_, and that the data is stored in the JSON format.
+
+> [!TIP]
+> ✎ If you're wondering why there's a `!` character in the above shell commands, that's because we're running them within a Jupyter notebook. Simply remove the prefix if you want to download and unzip the dataset within a terminal.
+
+To load a JSON file with the `load_dataset()` function, we just need to know if we're dealing with ordinary JSON (similar to a nested dictionary) or JSON Lines (line-separated JSON). Like many question answering datasets, SQuAD-it uses the nested format, with all the text stored in a `data` field. This means we can load the dataset by specifying the `field` argument as follows:
+
+```py
+from datasets import load_dataset
+
+squad_it_dataset = load_dataset("json", data_files="SQuAD_it-train.json", field="data")
+```
+
+By default, loading local files creates a `DatasetDict` object with a `train` split. We can see this by inspecting the `squad_it_dataset` object:
+
+```py
+squad_it_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['title', 'paragraphs'],
+ num_rows: 442
+ })
+})
+```
+
+This shows us the number of rows and the column names associated with the training set. We can view one of the examples by indexing into the `train` split as follows:
+
+```py
+squad_it_dataset["train"][0]
+```
+
+```python out
+{
+ "title": "Terremoto del Sichuan del 2008",
+ "paragraphs": [
+ {
+ "context": "Il terremoto del Sichuan del 2008 o il terremoto...",
+ "qas": [
+ {
+ "answers": [{"answer_start": 29, "text": "2008"}],
+ "id": "56cdca7862d2951400fa6826",
+ "question": "In quale anno si è verificato il terremoto nel Sichuan?",
+ },
+ ...
+ ],
+ },
+ ...
+ ],
+}
+```
+
+Great, we've loaded our first local dataset! But while this worked for the training set, what we really want is to include both the `train` and `test` splits in a single `DatasetDict` object so we can apply `Dataset.map()` functions across both splits at once. To do this, we can provide a dictionary to the `data_files` argument that maps each split name to a file associated with that split:
+
+```py
+data_files = {"train": "SQuAD_it-train.json", "test": "SQuAD_it-test.json"}
+squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
+squad_it_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['title', 'paragraphs'],
+ num_rows: 442
+ })
+ test: Dataset({
+ features: ['title', 'paragraphs'],
+ num_rows: 48
+ })
+})
+```
+
+This is exactly what we wanted. Now, we can apply various preprocessing techniques to clean up the data, tokenize the reviews, and so on.
+
+> [!TIP]
+> The `data_files` argument of the `load_dataset()` function is quite flexible and can be either a single file path, a list of file paths, or a dictionary that maps split names to file paths. You can also glob files that match a specified pattern according to the rules used by the Unix shell (e.g., you can glob all the JSON files in a directory as a single split by setting `data_files="*.json"`). See the 🤗 Datasets [documentation](https://huggingface.co/docs/datasets/loading#local-and-remote-files) for more details.
+
+The loading scripts in 🤗 Datasets actually support automatic decompression of the input files, so we could have skipped the use of `gzip` by pointing the `data_files` argument directly to the compressed files:
+
+```py
+data_files = {"train": "SQuAD_it-train.json.gz", "test": "SQuAD_it-test.json.gz"}
+squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
+```
+
+This can be useful if you don't want to manually decompress many GZIP files. The automatic decompression also applies to other common formats like ZIP and TAR, so you just need to point `data_files` to the compressed files and you're good to go!
+
+Now that you know how to load local files on your laptop or desktop, let's take a look at loading remote files.
+
+## Loading a remote dataset[[loading-a-remote-dataset]]
+
+If you're working as a data scientist or coder in a company, there's a good chance the datasets you want to analyze are stored on some remote server. Fortunately, loading remote files is just as simple as loading local ones! Instead of providing a path to local files, we point the `data_files` argument of `load_dataset()` to one or more URLs where the remote files are stored. For example, for the SQuAD-it dataset hosted on GitHub, we can just point `data_files` to the _SQuAD_it-*.json.gz_ URLs as follows:
+
+```py
+url = "https://github.com/crux82/squad-it/raw/master/"
+data_files = {
+ "train": url + "SQuAD_it-train.json.gz",
+ "test": url + "SQuAD_it-test.json.gz",
+}
+squad_it_dataset = load_dataset("json", data_files=data_files, field="data")
+```
+
+This returns the same `DatasetDict` object obtained above, but saves us the step of manually downloading and decompressing the _SQuAD_it-*.json.gz_ files. This wraps up our foray into the various ways to load datasets that aren't hosted on the Hugging Face Hub. Now that we've got a dataset to play with, let's get our hands dirty with various data-wrangling techniques!
+
+> [!TIP]
+> ✏️ **Try it out!** Pick another dataset hosted on GitHub or the [UCI Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php) and try loading it both locally and remotely using the techniques introduced above. For bonus points, try loading a dataset that’s stored in a CSV or text format (see the [documentation](https://huggingface.co/docs/datasets/loading#local-and-remote-files) for more information on these formats).
+
+
diff --git a/chapters/en/chapter5/3.mdx b/chapters/en/chapter5/3.mdx
index 88faf83e3..cc4c44a68 100644
--- a/chapters/en/chapter5/3.mdx
+++ b/chapters/en/chapter5/3.mdx
@@ -1,720 +1,720 @@
-# Time to slice and dice[[time-to-slice-and-dice]]
-
-
-
-Most of the time, the data you work with won't be perfectly prepared for training models. In this section we'll explore the various features that 🤗 Datasets provides to clean up your datasets.
-
-
-
-## Slicing and dicing our data[[slicing-and-dicing-our-data]]
-
-Similar to Pandas, 🤗 Datasets provides several functions to manipulate the contents of `Dataset` and `DatasetDict` objects. We already encountered the `Dataset.map()` method in [Chapter 3](/course/chapter3), and in this section we'll explore some of the other functions at our disposal.
-
-For this example we'll use the [Drug Review Dataset](https://archive.ics.uci.edu/ml/datasets/Drug+Review+Dataset+%28Drugs.com%29) that's hosted on the [UC Irvine Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php), which contains patient reviews on various drugs, along with the condition being treated and a 10-star rating of the patient's satisfaction.
-
-First we need to download and extract the data, which can be done with the `wget` and `unzip` commands:
-
-```py
-!wget "https://archive.ics.uci.edu/ml/machine-learning-databases/00462/drugsCom_raw.zip"
-!unzip drugsCom_raw.zip
-```
-
-Since TSV is just a variant of CSV that uses tabs instead of commas as the separator, we can load these files by using the `csv` loading script and specifying the `delimiter` argument in the `load_dataset()` function as follows:
-
-```py
-from datasets import load_dataset
-
-data_files = {"train": "drugsComTrain_raw.tsv", "test": "drugsComTest_raw.tsv"}
-# \t is the tab character in Python
-drug_dataset = load_dataset("csv", data_files=data_files, delimiter="\t")
-```
-
-A good practice when doing any sort of data analysis is to grab a small random sample to get a quick feel for the type of data you're working with. In 🤗 Datasets, we can create a random sample by chaining the `Dataset.shuffle()` and `Dataset.select()` functions together:
-
-```py
-drug_sample = drug_dataset["train"].shuffle(seed=42).select(range(1000))
-# Peek at the first few examples
-drug_sample[:3]
-```
-
-```python out
-{'Unnamed: 0': [87571, 178045, 80482],
- 'drugName': ['Naproxen', 'Duloxetine', 'Mobic'],
- 'condition': ['Gout, Acute', 'ibromyalgia', 'Inflammatory Conditions'],
- 'review': ['"like the previous person mention, I'm a strong believer of aleve, it works faster for my gout than the prescription meds I take. No more going to the doctor for refills.....Aleve works!"',
- '"I have taken Cymbalta for about a year and a half for fibromyalgia pain. It is great\r\nas a pain reducer and an anti-depressant, however, the side effects outweighed \r\nany benefit I got from it. I had trouble with restlessness, being tired constantly,\r\ndizziness, dry mouth, numbness and tingling in my feet, and horrible sweating. I am\r\nbeing weaned off of it now. Went from 60 mg to 30mg and now to 15 mg. I will be\r\noff completely in about a week. The fibro pain is coming back, but I would rather deal with it than the side effects."',
- '"I have been taking Mobic for over a year with no side effects other than an elevated blood pressure. I had severe knee and ankle pain which completely went away after taking Mobic. I attempted to stop the medication however pain returned after a few days."'],
- 'rating': [9.0, 3.0, 10.0],
- 'date': ['September 2, 2015', 'November 7, 2011', 'June 5, 2013'],
- 'usefulCount': [36, 13, 128]}
-```
-
-Note that we've fixed the seed in `Dataset.shuffle()` for reproducibility purposes. `Dataset.select()` expects an iterable of indices, so we've passed `range(1000)` to grab the first 1,000 examples from the shuffled dataset. From this sample we can already see a few quirks in our dataset:
-
-* The `Unnamed: 0` column looks suspiciously like an anonymized ID for each patient.
-* The `condition` column includes a mix of uppercase and lowercase labels.
-* The reviews are of varying length and contain a mix of Python line separators (`\r\n`) as well as HTML character codes like `&\#039;`.
-
-Let's see how we can use 🤗 Datasets to deal with each of these issues. To test the patient ID hypothesis for the `Unnamed: 0` column, we can use the `Dataset.unique()` function to verify that the number of IDs matches the number of rows in each split:
-
-```py
-for split in drug_dataset.keys():
- assert len(drug_dataset[split]) == len(drug_dataset[split].unique("Unnamed: 0"))
-```
-
-This seems to confirm our hypothesis, so let's clean up the dataset a bit by renaming the `Unnamed: 0` column to something a bit more interpretable. We can use the `DatasetDict.rename_column()` function to rename the column across both splits in one go:
-
-```py
-drug_dataset = drug_dataset.rename_column(
- original_column_name="Unnamed: 0", new_column_name="patient_id"
-)
-drug_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
- num_rows: 161297
- })
- test: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
- num_rows: 53766
- })
-})
-```
-
-> [!TIP]
-> ✏️ **Try it out!** Use the `Dataset.unique()` function to find the number of unique drugs and conditions in the training and test sets.
-
-Next, let's normalize all the `condition` labels using `Dataset.map()`. As we did with tokenization in [Chapter 3](/course/chapter3), we can define a simple function that can be applied across all the rows of each split in `drug_dataset`:
-
-```py
-def lowercase_condition(example):
- return {"condition": example["condition"].lower()}
-
-
-drug_dataset.map(lowercase_condition)
-```
-
-```python out
-AttributeError: 'NoneType' object has no attribute 'lower'
-```
-
-Oh no, we've run into a problem with our map function! From the error we can infer that some of the entries in the `condition` column are `None`, which cannot be lowercased as they're not strings. Let's drop these rows using `Dataset.filter()`, which works in a similar way to `Dataset.map()` and expects a function that receives a single example of the dataset. Instead of writing an explicit function like:
-
-```py
-def filter_nones(x):
- return x["condition"] is not None
-```
-
-and then running `drug_dataset.filter(filter_nones)`, we can do this in one line using a _lambda function_. In Python, lambda functions are small functions that you can define without explicitly naming them. They take the general form:
-
-```
-lambda :
-```
-
-where `lambda` is one of Python's special [keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords), `` is a list/set of comma-separated values that define the inputs to the function, and `` represents the operations you wish to execute. For example, we can define a simple lambda function that squares a number as follows:
-
-```
-lambda x : x * x
-```
-
-To apply this function to an input, we need to wrap it and the input in parentheses:
-
-```py
-(lambda x: x * x)(3)
-```
-
-```python out
-9
-```
-
-Similarly, we can define lambda functions with multiple arguments by separating them with commas. For example, we can compute the area of a triangle as follows:
-
-```py
-(lambda base, height: 0.5 * base * height)(4, 8)
-```
-
-```python out
-16.0
-```
-
-Lambda functions are handy when you want to define small, single-use functions (for more information about them, we recommend reading the excellent [Real Python tutorial](https://realpython.com/python-lambda/) by Andre Burgaud). In the 🤗 Datasets context, we can use lambda functions to define simple map and filter operations, so let's use this trick to eliminate the `None` entries in our dataset:
-
-```py
-drug_dataset = drug_dataset.filter(lambda x: x["condition"] is not None)
-```
-
-With the `None` entries removed, we can normalize our `condition` column:
-
-```py
-drug_dataset = drug_dataset.map(lowercase_condition)
-# Check that lowercasing worked
-drug_dataset["train"]["condition"][:3]
-```
-
-```python out
-['left ventricular dysfunction', 'adhd', 'birth control']
-```
-
-It works! Now that we've cleaned up the labels, let's take a look at cleaning up the reviews themselves.
-
-## Creating new columns[[creating-new-columns]]
-
-Whenever you're dealing with customer reviews, a good practice is to check the number of words in each review. A review might be just a single word like "Great!" or a full-blown essay with thousands of words, and depending on the use case you'll need to handle these extremes differently. To compute the number of words in each review, we'll use a rough heuristic based on splitting each text by whitespace.
-
-Let's define a simple function that counts the number of words in each review:
-
-```py
-def compute_review_length(example):
- return {"review_length": len(example["review"].split())}
-```
-
-Unlike our `lowercase_condition()` function, `compute_review_length()` returns a dictionary whose key does not correspond to one of the column names in the dataset. In this case, when `compute_review_length()` is passed to `Dataset.map()`, it will be applied to all the rows in the dataset to create a new `review_length` column:
-
-```py
-drug_dataset = drug_dataset.map(compute_review_length)
-# Inspect the first training example
-drug_dataset["train"][0]
-```
-
-```python out
-{'patient_id': 206461,
- 'drugName': 'Valsartan',
- 'condition': 'left ventricular dysfunction',
- 'review': '"It has no side effect, I take it in combination of Bystolic 5 Mg and Fish Oil"',
- 'rating': 9.0,
- 'date': 'May 20, 2012',
- 'usefulCount': 27,
- 'review_length': 17}
-```
-
-As expected, we can see a `review_length` column has been added to our training set. We can sort this new column with `Dataset.sort()` to see what the extreme values look like:
-
-```py
-drug_dataset["train"].sort("review_length")[:3]
-```
-
-```python out
-{'patient_id': [103488, 23627, 20558],
- 'drugName': ['Loestrin 21 1 / 20', 'Chlorzoxazone', 'Nucynta'],
- 'condition': ['birth control', 'muscle spasm', 'pain'],
- 'review': ['"Excellent."', '"useless"', '"ok"'],
- 'rating': [10.0, 1.0, 6.0],
- 'date': ['November 4, 2008', 'March 24, 2017', 'August 20, 2016'],
- 'usefulCount': [5, 2, 10],
- 'review_length': [1, 1, 1]}
-```
-
-As we suspected, some reviews contain just a single word, which, although it may be okay for sentiment analysis, would not be informative if we want to predict the condition.
-
-> [!TIP]
-> 🙋 An alternative way to add new columns to a dataset is with the `Dataset.add_column()` function. This allows you to provide the column as a Python list or NumPy array and can be handy in situations where `Dataset.map()` is not well suited for your analysis.
-
-Let's use the `Dataset.filter()` function to remove reviews that contain fewer than 30 words. Similarly to what we did with the `condition` column, we can filter out the very short reviews by requiring that the reviews have a length above this threshold:
-
-```py
-drug_dataset = drug_dataset.filter(lambda x: x["review_length"] > 30)
-print(drug_dataset.num_rows)
-```
-
-```python out
-{'train': 138514, 'test': 46108}
-```
-
-As you can see, this has removed around 15% of the reviews from our original training and test sets.
-
-> [!TIP]
-> ✏️ **Try it out!** Use the `Dataset.sort()` function to inspect the reviews with the largest numbers of words. See the [documentation](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.sort) to see which argument you need to use sort the reviews by length in descending order.
-
-The last thing we need to deal with is the presence of HTML character codes in our reviews. We can use Python's `html` module to unescape these characters, like so:
-
-```py
-import html
-
-text = "I'm a transformer called BERT"
-html.unescape(text)
-```
-
-```python out
-"I'm a transformer called BERT"
-```
-
-We'll use `Dataset.map()` to unescape all the HTML characters in our corpus:
-
-```python
-drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])})
-```
-
-As you can see, the `Dataset.map()` method is quite useful for processing data -- and we haven't even scratched the surface of everything it can do!
-
-## The `map()` method's superpowers[[the-map-methods-superpowers]]
-
-The `Dataset.map()` method takes a `batched` argument that, if set to `True`, causes it to send a batch of examples to the map function at once (the batch size is configurable but defaults to 1,000). For instance, the previous map function that unescaped all the HTML took a bit of time to run (you can read the time taken from the progress bars). We can speed this up by processing several elements at the same time using a list comprehension.
-
-When you specify `batched=True` the function receives a dictionary with the fields of the dataset, but each value is now a _list of values_, and not just a single value. The return value of `Dataset.map()` should be the same: a dictionary with the fields we want to update or add to our dataset, and a list of values. For example, here is another way to unescape all HTML characters, but using `batched=True`:
-
-```python
-new_drug_dataset = drug_dataset.map(
- lambda x: {"review": [html.unescape(o) for o in x["review"]]}, batched=True
-)
-```
-
-If you're running this code in a notebook, you'll see that this command executes way faster than the previous one. And it's not because our reviews have already been HTML-unescaped -- if you re-execute the instruction from the previous section (without `batched=True`), it will take the same amount of time as before. This is because list comprehensions are usually faster than executing the same code in a `for` loop, and we also gain some performance by accessing lots of elements at the same time instead of one by one.
-
-Using `Dataset.map()` with `batched=True` will be essential to unlock the speed of the "fast" tokenizers that we'll encounter in [Chapter 6](/course/chapter6), which can quickly tokenize big lists of texts. For instance, to tokenize all the drug reviews with a fast tokenizer, we could use a function like this:
-
-```python
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-
-
-def tokenize_function(examples):
- return tokenizer(examples["review"], truncation=True)
-```
-
-As you saw in [Chapter 3](/course/chapter3), we can pass one or several examples to the tokenizer, so we can use this function with or without `batched=True`. Let's take this opportunity to compare the performance of the different options. In a notebook, you can time a one-line instruction by adding `%time` before the line of code you wish to measure:
-
-```python no-format
-%time tokenized_dataset = drug_dataset.map(tokenize_function, batched=True)
-```
-
-You can also time a whole cell by putting `%%time` at the beginning of the cell. On the hardware we executed this on, it showed 10.8s for this instruction (it's the number written after "Wall time").
-
-> [!TIP]
-> ✏️ **Try it out!** Execute the same instruction with and without `batched=True`, then try it with a slow tokenizer (add `use_fast=False` in the `AutoTokenizer.from_pretrained()` method) so you can see what numbers you get on your hardware.
-
-Here are the results we obtained with and without batching, with a fast and a slow tokenizer:
-
-Options | Fast tokenizer | Slow tokenizer
-:--------------:|:--------------:|:-------------:
-`batched=True` | 10.8s | 4min41s
-`batched=False` | 59.2s | 5min3s
-
-This means that using a fast tokenizer with the `batched=True` option is 30 times faster than its slow counterpart with no batching -- this is truly amazing! That's the main reason why fast tokenizers are the default when using `AutoTokenizer` (and why they are called "fast"). They're able to achieve such a speedup because behind the scenes the tokenization code is executed in Rust, which is a language that makes it easy to parallelize code execution.
-
-Parallelization is also the reason for the nearly 6x speedup the fast tokenizer achieves with batching: you can't parallelize a single tokenization operation, but when you want to tokenize lots of texts at the same time you can just split the execution across several processes, each responsible for its own texts.
-
-`Dataset.map()` also has some parallelization capabilities of its own. Since they are not backed by Rust, they won't let a slow tokenizer catch up with a fast one, but they can still be helpful (especially if you're using a tokenizer that doesn't have a fast version). To enable multiprocessing, use the `num_proc` argument and specify the number of processes to use in your call to `Dataset.map()`:
-
-```py
-slow_tokenizer = AutoTokenizer.from_pretrained("bert-base-cased", use_fast=False)
-
-
-def slow_tokenize_function(examples):
- return slow_tokenizer(examples["review"], truncation=True)
-
-
-tokenized_dataset = drug_dataset.map(slow_tokenize_function, batched=True, num_proc=8)
-```
-
-You can experiment a little with timing to determine the optimal number of processes to use; in our case 8 seemed to produce the best speed gain. Here are the numbers we got with and without multiprocessing:
-
-Options | Fast tokenizer | Slow tokenizer
-:--------------:|:--------------:|:-------------:
-`batched=True` | 10.8s | 4min41s
-`batched=False` | 59.2s | 5min3s
-`batched=True`, `num_proc=8` | 6.52s | 41.3s
-`batched=False`, `num_proc=8` | 9.49s | 45.2s
-
-Those are much more reasonable results for the slow tokenizer, but the performance of the fast tokenizer was also substantially improved. Note, however, that won't always be the case -- for values of `num_proc` other than 8, our tests showed that it was faster to use `batched=True` without that option. In general, we don't recommend using Python multiprocessing for fast tokenizers with `batched=True`.
-
-> [!TIP]
-> Using `num_proc` to speed up your processing is usually a great idea, as long as the function you are using is not already doing some kind of multiprocessing of its own.
-
-All of this functionality condensed into a single method is already pretty amazing, but there's more! With `Dataset.map()` and `batched=True` you can change the number of elements in your dataset. This is super useful in many situations where you want to create several training features from one example, and we will need to do this as part of the preprocessing for several of the NLP tasks we'll undertake in [Chapter 7](/course/chapter7).
-
-> [!TIP]
-> 💡 In machine learning, an _example_ is usually defined as the set of _features_ that we feed to the model. In some contexts, these features will be the set of columns in a `Dataset`, but in others (like here and for question answering), multiple features can be extracted from a single example and belong to a single column.
-
-Let's have a look at how it works! Here we will tokenize our examples and truncate them to a maximum length of 128, but we will ask the tokenizer to return *all* the chunks of the texts instead of just the first one. This can be done with `return_overflowing_tokens=True`:
-
-```py
-def tokenize_and_split(examples):
- return tokenizer(
- examples["review"],
- truncation=True,
- max_length=128,
- return_overflowing_tokens=True,
- )
-```
-
-Let's test this on one example before using `Dataset.map()` on the whole dataset:
-
-```py
-result = tokenize_and_split(drug_dataset["train"][0])
-[len(inp) for inp in result["input_ids"]]
-```
-
-```python out
-[128, 49]
-```
-
-So, our first example in the training set became two features because it was tokenized to more than the maximum number of tokens we specified: the first one of length 128 and the second one of length 49. Now let's do this for all elements of the dataset!
-
-```py
-tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
-```
-
-```python out
-ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000
-```
-
-Oh no! That didn't work! Why not? Looking at the error message will give us a clue: there is a mismatch in the lengths of one of the columns, one being of length 1,463 and the other of length 1,000. If you've looked at the `Dataset.map()` [documentation](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map), you may recall that it's the number of samples passed to the function that we are mapping; here those 1,000 examples gave 1,463 new features, resulting in a shape error.
-
-The problem is that we're trying to mix two different datasets of different sizes: the `drug_dataset` columns will have a certain number of examples (the 1,000 in our error), but the `tokenized_dataset` we are building will have more (the 1,463 in the error message; it is more than 1,000 because we are tokenizing long reviews into more than one example by using `return_overflowing_tokens=True`). That doesn't work for a `Dataset`, so we need to either remove the columns from the old dataset or make them the same size as they are in the new dataset. We can do the former with the `remove_columns` argument:
-
-```py
-tokenized_dataset = drug_dataset.map(
- tokenize_and_split, batched=True, remove_columns=drug_dataset["train"].column_names
-)
-```
-
-Now this works without error. We can check that our new dataset has many more elements than the original dataset by comparing the lengths:
-
-```py
-len(tokenized_dataset["train"]), len(drug_dataset["train"])
-```
-
-```python out
-(206772, 138514)
-```
-
-We mentioned that we can also deal with the mismatched length problem by making the old columns the same size as the new ones. To do this, we will need the `overflow_to_sample_mapping` field the tokenizer returns when we set `return_overflowing_tokens=True`. It gives us a mapping from a new feature index to the index of the sample it originated from. Using this, we can associate each key present in our original dataset with a list of values of the right size by repeating the values of each example as many times as it generates new features:
-
-```py
-def tokenize_and_split(examples):
- result = tokenizer(
- examples["review"],
- truncation=True,
- max_length=128,
- return_overflowing_tokens=True,
- )
- # Extract mapping between new and old indices
- sample_map = result.pop("overflow_to_sample_mapping")
- for key, values in examples.items():
- result[key] = [values[i] for i in sample_map]
- return result
-```
-
-We can see it works with `Dataset.map()` without us needing to remove the old columns:
-
-```py
-tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
-tokenized_dataset
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
- num_rows: 206772
- })
- test: Dataset({
- features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
- num_rows: 68876
- })
-})
-```
-
-We get the same number of training features as before, but here we've kept all the old fields. If you need them for some post-processing after applying your model, you might want to use this approach.
-
-You've now seen how 🤗 Datasets can be used to preprocess a dataset in various ways. Although the processing functions of 🤗 Datasets will cover most of your model training needs,
-there may be times when you'll need to switch to Pandas to access more powerful features, like `DataFrame.groupby()` or high-level APIs for visualization. Fortunately, 🤗 Datasets is designed to be interoperable with libraries such as Pandas, NumPy, PyTorch, TensorFlow, and JAX. Let's take a look at how this works.
-
-## From `Dataset`s to `DataFrame`s and back[[from-datasets-to-dataframes-and-back]]
-
-
-
-To enable the conversion between various third-party libraries, 🤗 Datasets provides a `Dataset.set_format()` function. This function only changes the _output format_ of the dataset, so you can easily switch to another format without affecting the underlying _data format_, which is Apache Arrow. The formatting is done in place. To demonstrate, let's convert our dataset to Pandas:
-
-```py
-drug_dataset.set_format("pandas")
-```
-
-Now when we access elements of the dataset we get a `pandas.DataFrame` instead of a dictionary:
-
-```py
-drug_dataset["train"][:3]
-```
-
-
-
-
- |
- patient_id |
- drugName |
- condition |
- review |
- rating |
- date |
- usefulCount |
- review_length |
-
-
-
-
- | 0 |
- 95260 |
- Guanfacine |
- adhd |
- "My son is halfway through his fourth week of Intuniv..." |
- 8.0 |
- April 27, 2010 |
- 192 |
- 141 |
-
-
- | 1 |
- 92703 |
- Lybrel |
- birth control |
- "I used to take another oral contraceptive, which had 21 pill cycle, and was very happy- very light periods, max 5 days, no other side effects..." |
- 5.0 |
- December 14, 2009 |
- 17 |
- 134 |
-
-
- | 2 |
- 138000 |
- Ortho Evra |
- birth control |
- "This is my first time using any form of birth control..." |
- 8.0 |
- November 3, 2015 |
- 10 |
- 89 |
-
-
-
-
-Let's create a `pandas.DataFrame` for the whole training set by selecting all the elements of `drug_dataset["train"]`:
-
-```py
-train_df = drug_dataset["train"][:]
-```
-
-> [!TIP]
-> 🚨 Under the hood, `Dataset.set_format()` changes the return format for the dataset's `__getitem__()` dunder method. This means that when we want to create a new object like `train_df` from a `Dataset` in the `"pandas"` format, we need to slice the whole dataset to obtain a `pandas.DataFrame`. You can verify for yourself that the type of `drug_dataset["train"]` is `Dataset`, irrespective of the output format.
-
-
-From here we can use all the Pandas functionality that we want. For example, we can do fancy chaining to compute the class distribution among the `condition` entries:
-
-```py
-frequencies = (
- train_df["condition"]
- .value_counts()
- .to_frame()
- .reset_index()
- .rename(columns={"index": "condition", "count": "frequency"})
-)
-frequencies.head()
-```
-
-
-
-
- |
- condition |
- frequency |
-
-
-
-
- | 0 |
- birth control |
- 27655 |
-
-
- | 1 |
- depression |
- 8023 |
-
-
- | 2 |
- acne |
- 5209 |
-
-
- | 3 |
- anxiety |
- 4991 |
-
-
- | 4 |
- pain |
- 4744 |
-
-
-
-
-
-And once we're done with our Pandas analysis, we can always create a new `Dataset` object by using the `Dataset.from_pandas()` function as follows:
-
-
-```py
-from datasets import Dataset
-
-freq_dataset = Dataset.from_pandas(frequencies)
-freq_dataset
-```
-
-```python out
-Dataset({
- features: ['condition', 'frequency'],
- num_rows: 819
-})
-```
-
-> [!TIP]
-> ✏️ **Try it out!** Compute the average rating per drug and store the result in a new `Dataset`.
-
-This wraps up our tour of the various preprocessing techniques available in 🤗 Datasets. To round out the section, let's create a validation set to prepare the dataset for training a classifier on. Before doing so, we'll reset the output format of `drug_dataset` from `"pandas"` to `"arrow"`:
-
-```python
-drug_dataset.reset_format()
-```
-
-## Creating a validation set[[creating-a-validation-set]]
-
-Although we have a test set we could use for evaluation, it's a good practice to leave the test set untouched and create a separate validation set during development. Once you are happy with the performance of your models on the validation set, you can do a final sanity check on the test set. This process helps mitigate the risk that you'll overfit to the test set and deploy a model that fails on real-world data.
-
-🤗 Datasets provides a `Dataset.train_test_split()` function that is based on the famous functionality from `scikit-learn`. Let's use it to split our training set into `train` and `validation` splits (we set the `seed` argument for reproducibility):
-
-```py
-drug_dataset_clean = drug_dataset["train"].train_test_split(train_size=0.8, seed=42)
-# Rename the default "test" split to "validation"
-drug_dataset_clean["validation"] = drug_dataset_clean.pop("test")
-# Add the "test" set to our `DatasetDict`
-drug_dataset_clean["test"] = drug_dataset["test"]
-drug_dataset_clean
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
- num_rows: 110811
- })
- validation: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
- num_rows: 27703
- })
- test: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
- num_rows: 46108
- })
-})
-```
-
-Great, we've now prepared a dataset that's ready for training some models on! In [section 5](/course/chapter5/5) we'll show you how to upload datasets to the Hugging Face Hub, but for now let's cap off our analysis by looking at a few ways you can save datasets on your local machine.
-
-## Saving a dataset[[saving-a-dataset]]
-
-
-
-Although 🤗 Datasets will cache every downloaded dataset and the operations performed on it, there are times when you'll want to save a dataset to disk (e.g., in case the cache gets deleted). As shown in the table below, 🤗 Datasets provides three main functions to save your dataset in different formats:
-
-| Data format | Function |
-| :---------: | :--------------------: |
-| Arrow | `Dataset.save_to_disk()` |
-| CSV | `Dataset.to_csv()` |
-| JSON | `Dataset.to_json()` |
-
-For example, let's save our cleaned dataset in the Arrow format:
-
-```py
-drug_dataset_clean.save_to_disk("drug-reviews")
-```
-
-This will create a directory with the following structure:
-
-```
-drug-reviews/
-├── dataset_dict.json
-├── test
-│ ├── dataset.arrow
-│ ├── dataset_info.json
-│ └── state.json
-├── train
-│ ├── dataset.arrow
-│ ├── dataset_info.json
-│ ├── indices.arrow
-│ └── state.json
-└── validation
- ├── dataset.arrow
- ├── dataset_info.json
- ├── indices.arrow
- └── state.json
-```
-
-where we can see that each split is associated with its own *dataset.arrow* table, and some metadata in *dataset_info.json* and *state.json*. You can think of the Arrow format as a fancy table of columns and rows that is optimized for building high-performance applications that process and transport large datasets.
-
-Once the dataset is saved, we can load it by using the `load_from_disk()` function as follows:
-
-```py
-from datasets import load_from_disk
-
-drug_dataset_reloaded = load_from_disk("drug-reviews")
-drug_dataset_reloaded
-```
-
-```python out
-DatasetDict({
- train: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
- num_rows: 110811
- })
- validation: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
- num_rows: 27703
- })
- test: Dataset({
- features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
- num_rows: 46108
- })
-})
-```
-
-For the CSV and JSON formats, we have to store each split as a separate file. One way to do this is by iterating over the keys and values in the `DatasetDict` object:
-
-```py
-for split, dataset in drug_dataset_clean.items():
- dataset.to_json(f"drug-reviews-{split}.jsonl")
-```
-
-This saves each split in [JSON Lines format](https://jsonlines.org), where each row in the dataset is stored as a single line of JSON. Here's what the first example looks like:
-
-```py
-!head -n 1 drug-reviews-train.jsonl
-```
-
-```python out
-{"patient_id":141780,"drugName":"Escitalopram","condition":"depression","review":"\"I seemed to experience the regular side effects of LEXAPRO, insomnia, low sex drive, sleepiness during the day. I am taking it at night because my doctor said if it made me tired to take it at night. I assumed it would and started out taking it at night. Strange dreams, some pleasant. I was diagnosed with fibromyalgia. Seems to be helping with the pain. Have had anxiety and depression in my family, and have tried quite a few other medications that haven't worked. Only have been on it for two weeks but feel more positive in my mind, want to accomplish more in my life. Hopefully the side effects will dwindle away, worth it to stick with it from hearing others responses. Great medication.\"","rating":9.0,"date":"May 29, 2011","usefulCount":10,"review_length":125}
-```
-
-We can then use the techniques from [section 2](/course/chapter5/2) to load the JSON files as follows:
-
-```py
-data_files = {
- "train": "drug-reviews-train.jsonl",
- "validation": "drug-reviews-validation.jsonl",
- "test": "drug-reviews-test.jsonl",
-}
-drug_dataset_reloaded = load_dataset("json", data_files=data_files)
-```
-
-And that's it for our excursion into data wrangling with 🤗 Datasets! Now that we have a cleaned dataset for training a model on, here are a few ideas that you could try out:
-
-1. Use the techniques from [Chapter 3](/course/chapter3) to train a classifier that can predict the patient condition based on the drug review.
-2. Use the `summarization` pipeline from [Chapter 1](/course/chapter1) to generate summaries of the reviews.
-
-Next, we'll take a look at how 🤗 Datasets can enable you to work with huge datasets without blowing up your laptop!
+# Time to slice and dice[[time-to-slice-and-dice]]
+
+
+
+Most of the time, the data you work with won't be perfectly prepared for training models. In this section we'll explore the various features that 🤗 Datasets provides to clean up your datasets.
+
+
+
+## Slicing and dicing our data[[slicing-and-dicing-our-data]]
+
+Similar to Pandas, 🤗 Datasets provides several functions to manipulate the contents of `Dataset` and `DatasetDict` objects. We already encountered the `Dataset.map()` method in [Chapter 3](/course/chapter3), and in this section we'll explore some of the other functions at our disposal.
+
+For this example we'll use the [Drug Review Dataset](https://archive.ics.uci.edu/ml/datasets/Drug+Review+Dataset+%28Drugs.com%29) that's hosted on the [UC Irvine Machine Learning Repository](https://archive.ics.uci.edu/ml/index.php), which contains patient reviews on various drugs, along with the condition being treated and a 10-star rating of the patient's satisfaction.
+
+First we need to download and extract the data, which can be done with the `wget` and `unzip` commands:
+
+```py
+!wget "https://archive.ics.uci.edu/ml/machine-learning-databases/00462/drugsCom_raw.zip"
+!unzip drugsCom_raw.zip
+```
+
+Since TSV is just a variant of CSV that uses tabs instead of commas as the separator, we can load these files by using the `csv` loading script and specifying the `delimiter` argument in the `load_dataset()` function as follows:
+
+```py
+from datasets import load_dataset
+
+data_files = {"train": "drugsComTrain_raw.tsv", "test": "drugsComTest_raw.tsv"}
+# \t is the tab character in Python
+drug_dataset = load_dataset("csv", data_files=data_files, delimiter="\t")
+```
+
+A good practice when doing any sort of data analysis is to grab a small random sample to get a quick feel for the type of data you're working with. In 🤗 Datasets, we can create a random sample by chaining the `Dataset.shuffle()` and `Dataset.select()` functions together:
+
+```py
+drug_sample = drug_dataset["train"].shuffle(seed=42).select(range(1000))
+# Peek at the first few examples
+drug_sample[:3]
+```
+
+```python out
+{'Unnamed: 0': [87571, 178045, 80482],
+ 'drugName': ['Naproxen', 'Duloxetine', 'Mobic'],
+ 'condition': ['Gout, Acute', 'ibromyalgia', 'Inflammatory Conditions'],
+ 'review': ['"like the previous person mention, I'm a strong believer of aleve, it works faster for my gout than the prescription meds I take. No more going to the doctor for refills.....Aleve works!"',
+ '"I have taken Cymbalta for about a year and a half for fibromyalgia pain. It is great\r\nas a pain reducer and an anti-depressant, however, the side effects outweighed \r\nany benefit I got from it. I had trouble with restlessness, being tired constantly,\r\ndizziness, dry mouth, numbness and tingling in my feet, and horrible sweating. I am\r\nbeing weaned off of it now. Went from 60 mg to 30mg and now to 15 mg. I will be\r\noff completely in about a week. The fibro pain is coming back, but I would rather deal with it than the side effects."',
+ '"I have been taking Mobic for over a year with no side effects other than an elevated blood pressure. I had severe knee and ankle pain which completely went away after taking Mobic. I attempted to stop the medication however pain returned after a few days."'],
+ 'rating': [9.0, 3.0, 10.0],
+ 'date': ['September 2, 2015', 'November 7, 2011', 'June 5, 2013'],
+ 'usefulCount': [36, 13, 128]}
+```
+
+Note that we've fixed the seed in `Dataset.shuffle()` for reproducibility purposes. `Dataset.select()` expects an iterable of indices, so we've passed `range(1000)` to grab the first 1,000 examples from the shuffled dataset. From this sample we can already see a few quirks in our dataset:
+
+* The `Unnamed: 0` column looks suspiciously like an anonymized ID for each patient.
+* The `condition` column includes a mix of uppercase and lowercase labels.
+* The reviews are of varying length and contain a mix of Python line separators (`\r\n`) as well as HTML character codes like `&\#039;`.
+
+Let's see how we can use 🤗 Datasets to deal with each of these issues. To test the patient ID hypothesis for the `Unnamed: 0` column, we can use the `Dataset.unique()` function to verify that the number of IDs matches the number of rows in each split:
+
+```py
+for split in drug_dataset.keys():
+ assert len(drug_dataset[split]) == len(drug_dataset[split].unique("Unnamed: 0"))
+```
+
+This seems to confirm our hypothesis, so let's clean up the dataset a bit by renaming the `Unnamed: 0` column to something a bit more interpretable. We can use the `DatasetDict.rename_column()` function to rename the column across both splits in one go:
+
+```py
+drug_dataset = drug_dataset.rename_column(
+ original_column_name="Unnamed: 0", new_column_name="patient_id"
+)
+drug_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
+ num_rows: 161297
+ })
+ test: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount'],
+ num_rows: 53766
+ })
+})
+```
+
+> [!TIP]
+> ✏️ **Try it out!** Use the `Dataset.unique()` function to find the number of unique drugs and conditions in the training and test sets.
+
+Next, let's normalize all the `condition` labels using `Dataset.map()`. As we did with tokenization in [Chapter 3](/course/chapter3), we can define a simple function that can be applied across all the rows of each split in `drug_dataset`:
+
+```py
+def lowercase_condition(example):
+ return {"condition": example["condition"].lower()}
+
+
+drug_dataset.map(lowercase_condition)
+```
+
+```python out
+AttributeError: 'NoneType' object has no attribute 'lower'
+```
+
+Oh no, we've run into a problem with our map function! From the error we can infer that some of the entries in the `condition` column are `None`, which cannot be lowercased as they're not strings. Let's drop these rows using `Dataset.filter()`, which works in a similar way to `Dataset.map()` and expects a function that receives a single example of the dataset. Instead of writing an explicit function like:
+
+```py
+def filter_nones(x):
+ return x["condition"] is not None
+```
+
+and then running `drug_dataset.filter(filter_nones)`, we can do this in one line using a _lambda function_. In Python, lambda functions are small functions that you can define without explicitly naming them. They take the general form:
+
+```
+lambda :
+```
+
+where `lambda` is one of Python's special [keywords](https://docs.python.org/3/reference/lexical_analysis.html#keywords), `` is a list/set of comma-separated values that define the inputs to the function, and `` represents the operations you wish to execute. For example, we can define a simple lambda function that squares a number as follows:
+
+```
+lambda x : x * x
+```
+
+To apply this function to an input, we need to wrap it and the input in parentheses:
+
+```py
+(lambda x: x * x)(3)
+```
+
+```python out
+9
+```
+
+Similarly, we can define lambda functions with multiple arguments by separating them with commas. For example, we can compute the area of a triangle as follows:
+
+```py
+(lambda base, height: 0.5 * base * height)(4, 8)
+```
+
+```python out
+16.0
+```
+
+Lambda functions are handy when you want to define small, single-use functions (for more information about them, we recommend reading the excellent [Real Python tutorial](https://realpython.com/python-lambda/) by Andre Burgaud). In the 🤗 Datasets context, we can use lambda functions to define simple map and filter operations, so let's use this trick to eliminate the `None` entries in our dataset:
+
+```py
+drug_dataset = drug_dataset.filter(lambda x: x["condition"] is not None)
+```
+
+With the `None` entries removed, we can normalize our `condition` column:
+
+```py
+drug_dataset = drug_dataset.map(lowercase_condition)
+# Check that lowercasing worked
+drug_dataset["train"]["condition"][:3]
+```
+
+```python out
+['left ventricular dysfunction', 'adhd', 'birth control']
+```
+
+It works! Now that we've cleaned up the labels, let's take a look at cleaning up the reviews themselves.
+
+## Creating new columns[[creating-new-columns]]
+
+Whenever you're dealing with customer reviews, a good practice is to check the number of words in each review. A review might be just a single word like "Great!" or a full-blown essay with thousands of words, and depending on the use case you'll need to handle these extremes differently. To compute the number of words in each review, we'll use a rough heuristic based on splitting each text by whitespace.
+
+Let's define a simple function that counts the number of words in each review:
+
+```py
+def compute_review_length(example):
+ return {"review_length": len(example["review"].split())}
+```
+
+Unlike our `lowercase_condition()` function, `compute_review_length()` returns a dictionary whose key does not correspond to one of the column names in the dataset. In this case, when `compute_review_length()` is passed to `Dataset.map()`, it will be applied to all the rows in the dataset to create a new `review_length` column:
+
+```py
+drug_dataset = drug_dataset.map(compute_review_length)
+# Inspect the first training example
+drug_dataset["train"][0]
+```
+
+```python out
+{'patient_id': 206461,
+ 'drugName': 'Valsartan',
+ 'condition': 'left ventricular dysfunction',
+ 'review': '"It has no side effect, I take it in combination of Bystolic 5 Mg and Fish Oil"',
+ 'rating': 9.0,
+ 'date': 'May 20, 2012',
+ 'usefulCount': 27,
+ 'review_length': 17}
+```
+
+As expected, we can see a `review_length` column has been added to our training set. We can sort this new column with `Dataset.sort()` to see what the extreme values look like:
+
+```py
+drug_dataset["train"].sort("review_length")[:3]
+```
+
+```python out
+{'patient_id': [103488, 23627, 20558],
+ 'drugName': ['Loestrin 21 1 / 20', 'Chlorzoxazone', 'Nucynta'],
+ 'condition': ['birth control', 'muscle spasm', 'pain'],
+ 'review': ['"Excellent."', '"useless"', '"ok"'],
+ 'rating': [10.0, 1.0, 6.0],
+ 'date': ['November 4, 2008', 'March 24, 2017', 'August 20, 2016'],
+ 'usefulCount': [5, 2, 10],
+ 'review_length': [1, 1, 1]}
+```
+
+As we suspected, some reviews contain just a single word, which, although it may be okay for sentiment analysis, would not be informative if we want to predict the condition.
+
+> [!TIP]
+> 🙋 An alternative way to add new columns to a dataset is with the `Dataset.add_column()` function. This allows you to provide the column as a Python list or NumPy array and can be handy in situations where `Dataset.map()` is not well suited for your analysis.
+
+Let's use the `Dataset.filter()` function to remove reviews that contain fewer than 30 words. Similarly to what we did with the `condition` column, we can filter out the very short reviews by requiring that the reviews have a length above this threshold:
+
+```py
+drug_dataset = drug_dataset.filter(lambda x: x["review_length"] > 30)
+print(drug_dataset.num_rows)
+```
+
+```python out
+{'train': 138514, 'test': 46108}
+```
+
+As you can see, this has removed around 15% of the reviews from our original training and test sets.
+
+> [!TIP]
+> ✏️ **Try it out!** Use the `Dataset.sort()` function to inspect the reviews with the largest numbers of words. See the [documentation](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.sort) to see which argument you need to use sort the reviews by length in descending order.
+
+The last thing we need to deal with is the presence of HTML character codes in our reviews. We can use Python's `html` module to unescape these characters, like so:
+
+```py
+import html
+
+text = "I'm a transformer called BERT"
+html.unescape(text)
+```
+
+```python out
+"I'm a transformer called BERT"
+```
+
+We'll use `Dataset.map()` to unescape all the HTML characters in our corpus:
+
+```python
+drug_dataset = drug_dataset.map(lambda x: {"review": html.unescape(x["review"])})
+```
+
+As you can see, the `Dataset.map()` method is quite useful for processing data -- and we haven't even scratched the surface of everything it can do!
+
+## The `map()` method's superpowers[[the-map-methods-superpowers]]
+
+The `Dataset.map()` method takes a `batched` argument that, if set to `True`, causes it to send a batch of examples to the map function at once (the batch size is configurable but defaults to 1,000). For instance, the previous map function that unescaped all the HTML took a bit of time to run (you can read the time taken from the progress bars). We can speed this up by processing several elements at the same time using a list comprehension.
+
+When you specify `batched=True` the function receives a dictionary with the fields of the dataset, but each value is now a _list of values_, and not just a single value. The return value of `Dataset.map()` should be the same: a dictionary with the fields we want to update or add to our dataset, and a list of values. For example, here is another way to unescape all HTML characters, but using `batched=True`:
+
+```python
+new_drug_dataset = drug_dataset.map(
+ lambda x: {"review": [html.unescape(o) for o in x["review"]]}, batched=True
+)
+```
+
+If you're running this code in a notebook, you'll see that this command executes way faster than the previous one. And it's not because our reviews have already been HTML-unescaped -- if you re-execute the instruction from the previous section (without `batched=True`), it will take the same amount of time as before. This is because list comprehensions are usually faster than executing the same code in a `for` loop, and we also gain some performance by accessing lots of elements at the same time instead of one by one.
+
+Using `Dataset.map()` with `batched=True` will be essential to unlock the speed of the "fast" tokenizers that we'll encounter in [Chapter 6](/course/chapter6), which can quickly tokenize big lists of texts. For instance, to tokenize all the drug reviews with a fast tokenizer, we could use a function like this:
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+
+
+def tokenize_function(examples):
+ return tokenizer(examples["review"], truncation=True)
+```
+
+As you saw in [Chapter 3](/course/chapter3), we can pass one or several examples to the tokenizer, so we can use this function with or without `batched=True`. Let's take this opportunity to compare the performance of the different options. In a notebook, you can time a one-line instruction by adding `%time` before the line of code you wish to measure:
+
+```python no-format
+%time tokenized_dataset = drug_dataset.map(tokenize_function, batched=True)
+```
+
+You can also time a whole cell by putting `%%time` at the beginning of the cell. On the hardware we executed this on, it showed 10.8s for this instruction (it's the number written after "Wall time").
+
+> [!TIP]
+> ✏️ **Try it out!** Execute the same instruction with and without `batched=True`, then try it with a slow tokenizer (add `use_fast=False` in the `AutoTokenizer.from_pretrained()` method) so you can see what numbers you get on your hardware.
+
+Here are the results we obtained with and without batching, with a fast and a slow tokenizer:
+
+Options | Fast tokenizer | Slow tokenizer
+:--------------:|:--------------:|:-------------:
+`batched=True` | 10.8s | 4min41s
+`batched=False` | 59.2s | 5min3s
+
+This means that using a fast tokenizer with the `batched=True` option is 30 times faster than its slow counterpart with no batching -- this is truly amazing! That's the main reason why fast tokenizers are the default when using `AutoTokenizer` (and why they are called "fast"). They're able to achieve such a speedup because behind the scenes the tokenization code is executed in Rust, which is a language that makes it easy to parallelize code execution.
+
+Parallelization is also the reason for the nearly 6x speedup the fast tokenizer achieves with batching: you can't parallelize a single tokenization operation, but when you want to tokenize lots of texts at the same time you can just split the execution across several processes, each responsible for its own texts.
+
+`Dataset.map()` also has some parallelization capabilities of its own. Since they are not backed by Rust, they won't let a slow tokenizer catch up with a fast one, but they can still be helpful (especially if you're using a tokenizer that doesn't have a fast version). To enable multiprocessing, use the `num_proc` argument and specify the number of processes to use in your call to `Dataset.map()`:
+
+```py
+slow_tokenizer = AutoTokenizer.from_pretrained("bert-base-cased", use_fast=False)
+
+
+def slow_tokenize_function(examples):
+ return slow_tokenizer(examples["review"], truncation=True)
+
+
+tokenized_dataset = drug_dataset.map(slow_tokenize_function, batched=True, num_proc=8)
+```
+
+You can experiment a little with timing to determine the optimal number of processes to use; in our case 8 seemed to produce the best speed gain. Here are the numbers we got with and without multiprocessing:
+
+Options | Fast tokenizer | Slow tokenizer
+:--------------:|:--------------:|:-------------:
+`batched=True` | 10.8s | 4min41s
+`batched=False` | 59.2s | 5min3s
+`batched=True`, `num_proc=8` | 6.52s | 41.3s
+`batched=False`, `num_proc=8` | 9.49s | 45.2s
+
+Those are much more reasonable results for the slow tokenizer, but the performance of the fast tokenizer was also substantially improved. Note, however, that won't always be the case -- for values of `num_proc` other than 8, our tests showed that it was faster to use `batched=True` without that option. In general, we don't recommend using Python multiprocessing for fast tokenizers with `batched=True`.
+
+> [!TIP]
+> Using `num_proc` to speed up your processing is usually a great idea, as long as the function you are using is not already doing some kind of multiprocessing of its own.
+
+All of this functionality condensed into a single method is already pretty amazing, but there's more! With `Dataset.map()` and `batched=True` you can change the number of elements in your dataset. This is super useful in many situations where you want to create several training features from one example, and we will need to do this as part of the preprocessing for several of the NLP tasks we'll undertake in [Chapter 7](/course/chapter7).
+
+> [!TIP]
+> 💡 In machine learning, an _example_ is usually defined as the set of _features_ that we feed to the model. In some contexts, these features will be the set of columns in a `Dataset`, but in others (like here and for question answering), multiple features can be extracted from a single example and belong to a single column.
+
+Let's have a look at how it works! Here we will tokenize our examples and truncate them to a maximum length of 128, but we will ask the tokenizer to return *all* the chunks of the texts instead of just the first one. This can be done with `return_overflowing_tokens=True`:
+
+```py
+def tokenize_and_split(examples):
+ return tokenizer(
+ examples["review"],
+ truncation=True,
+ max_length=128,
+ return_overflowing_tokens=True,
+ )
+```
+
+Let's test this on one example before using `Dataset.map()` on the whole dataset:
+
+```py
+result = tokenize_and_split(drug_dataset["train"][0])
+[len(inp) for inp in result["input_ids"]]
+```
+
+```python out
+[128, 49]
+```
+
+So, our first example in the training set became two features because it was tokenized to more than the maximum number of tokens we specified: the first one of length 128 and the second one of length 49. Now let's do this for all elements of the dataset!
+
+```py
+tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
+```
+
+```python out
+ArrowInvalid: Column 1 named condition expected length 1463 but got length 1000
+```
+
+Oh no! That didn't work! Why not? Looking at the error message will give us a clue: there is a mismatch in the lengths of one of the columns, one being of length 1,463 and the other of length 1,000. If you've looked at the `Dataset.map()` [documentation](https://huggingface.co/docs/datasets/package_reference/main_classes#datasets.Dataset.map), you may recall that it's the number of samples passed to the function that we are mapping; here those 1,000 examples gave 1,463 new features, resulting in a shape error.
+
+The problem is that we're trying to mix two different datasets of different sizes: the `drug_dataset` columns will have a certain number of examples (the 1,000 in our error), but the `tokenized_dataset` we are building will have more (the 1,463 in the error message; it is more than 1,000 because we are tokenizing long reviews into more than one example by using `return_overflowing_tokens=True`). That doesn't work for a `Dataset`, so we need to either remove the columns from the old dataset or make them the same size as they are in the new dataset. We can do the former with the `remove_columns` argument:
+
+```py
+tokenized_dataset = drug_dataset.map(
+ tokenize_and_split, batched=True, remove_columns=drug_dataset["train"].column_names
+)
+```
+
+Now this works without error. We can check that our new dataset has many more elements than the original dataset by comparing the lengths:
+
+```py
+len(tokenized_dataset["train"]), len(drug_dataset["train"])
+```
+
+```python out
+(206772, 138514)
+```
+
+We mentioned that we can also deal with the mismatched length problem by making the old columns the same size as the new ones. To do this, we will need the `overflow_to_sample_mapping` field the tokenizer returns when we set `return_overflowing_tokens=True`. It gives us a mapping from a new feature index to the index of the sample it originated from. Using this, we can associate each key present in our original dataset with a list of values of the right size by repeating the values of each example as many times as it generates new features:
+
+```py
+def tokenize_and_split(examples):
+ result = tokenizer(
+ examples["review"],
+ truncation=True,
+ max_length=128,
+ return_overflowing_tokens=True,
+ )
+ # Extract mapping between new and old indices
+ sample_map = result.pop("overflow_to_sample_mapping")
+ for key, values in examples.items():
+ result[key] = [values[i] for i in sample_map]
+ return result
+```
+
+We can see it works with `Dataset.map()` without us needing to remove the old columns:
+
+```py
+tokenized_dataset = drug_dataset.map(tokenize_and_split, batched=True)
+tokenized_dataset
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
+ num_rows: 206772
+ })
+ test: Dataset({
+ features: ['attention_mask', 'condition', 'date', 'drugName', 'input_ids', 'patient_id', 'rating', 'review', 'review_length', 'token_type_ids', 'usefulCount'],
+ num_rows: 68876
+ })
+})
+```
+
+We get the same number of training features as before, but here we've kept all the old fields. If you need them for some post-processing after applying your model, you might want to use this approach.
+
+You've now seen how 🤗 Datasets can be used to preprocess a dataset in various ways. Although the processing functions of 🤗 Datasets will cover most of your model training needs,
+there may be times when you'll need to switch to Pandas to access more powerful features, like `DataFrame.groupby()` or high-level APIs for visualization. Fortunately, 🤗 Datasets is designed to be interoperable with libraries such as Pandas, NumPy, PyTorch, TensorFlow, and JAX. Let's take a look at how this works.
+
+## From `Dataset`s to `DataFrame`s and back[[from-datasets-to-dataframes-and-back]]
+
+
+
+To enable the conversion between various third-party libraries, 🤗 Datasets provides a `Dataset.set_format()` function. This function only changes the _output format_ of the dataset, so you can easily switch to another format without affecting the underlying _data format_, which is Apache Arrow. The formatting is done in place. To demonstrate, let's convert our dataset to Pandas:
+
+```py
+drug_dataset.set_format("pandas")
+```
+
+Now when we access elements of the dataset we get a `pandas.DataFrame` instead of a dictionary:
+
+```py
+drug_dataset["train"][:3]
+```
+
+
+
+
+ |
+ patient_id |
+ drugName |
+ condition |
+ review |
+ rating |
+ date |
+ usefulCount |
+ review_length |
+
+
+
+
+ | 0 |
+ 95260 |
+ Guanfacine |
+ adhd |
+ "My son is halfway through his fourth week of Intuniv..." |
+ 8.0 |
+ April 27, 2010 |
+ 192 |
+ 141 |
+
+
+ | 1 |
+ 92703 |
+ Lybrel |
+ birth control |
+ "I used to take another oral contraceptive, which had 21 pill cycle, and was very happy- very light periods, max 5 days, no other side effects..." |
+ 5.0 |
+ December 14, 2009 |
+ 17 |
+ 134 |
+
+
+ | 2 |
+ 138000 |
+ Ortho Evra |
+ birth control |
+ "This is my first time using any form of birth control..." |
+ 8.0 |
+ November 3, 2015 |
+ 10 |
+ 89 |
+
+
+
+
+Let's create a `pandas.DataFrame` for the whole training set by selecting all the elements of `drug_dataset["train"]`:
+
+```py
+train_df = drug_dataset["train"][:]
+```
+
+> [!TIP]
+> 🚨 Under the hood, `Dataset.set_format()` changes the return format for the dataset's `__getitem__()` dunder method. This means that when we want to create a new object like `train_df` from a `Dataset` in the `"pandas"` format, we need to slice the whole dataset to obtain a `pandas.DataFrame`. You can verify for yourself that the type of `drug_dataset["train"]` is `Dataset`, irrespective of the output format.
+
+
+From here we can use all the Pandas functionality that we want. For example, we can do fancy chaining to compute the class distribution among the `condition` entries:
+
+```py
+frequencies = (
+ train_df["condition"]
+ .value_counts()
+ .to_frame()
+ .reset_index()
+ .rename(columns={"index": "condition", "count": "frequency"})
+)
+frequencies.head()
+```
+
+
+
+
+ |
+ condition |
+ frequency |
+
+
+
+
+ | 0 |
+ birth control |
+ 27655 |
+
+
+ | 1 |
+ depression |
+ 8023 |
+
+
+ | 2 |
+ acne |
+ 5209 |
+
+
+ | 3 |
+ anxiety |
+ 4991 |
+
+
+ | 4 |
+ pain |
+ 4744 |
+
+
+
+
+
+And once we're done with our Pandas analysis, we can always create a new `Dataset` object by using the `Dataset.from_pandas()` function as follows:
+
+
+```py
+from datasets import Dataset
+
+freq_dataset = Dataset.from_pandas(frequencies)
+freq_dataset
+```
+
+```python out
+Dataset({
+ features: ['condition', 'frequency'],
+ num_rows: 819
+})
+```
+
+> [!TIP]
+> ✏️ **Try it out!** Compute the average rating per drug and store the result in a new `Dataset`.
+
+This wraps up our tour of the various preprocessing techniques available in 🤗 Datasets. To round out the section, let's create a validation set to prepare the dataset for training a classifier on. Before doing so, we'll reset the output format of `drug_dataset` from `"pandas"` to `"arrow"`:
+
+```python
+drug_dataset.reset_format()
+```
+
+## Creating a validation set[[creating-a-validation-set]]
+
+Although we have a test set we could use for evaluation, it's a good practice to leave the test set untouched and create a separate validation set during development. Once you are happy with the performance of your models on the validation set, you can do a final sanity check on the test set. This process helps mitigate the risk that you'll overfit to the test set and deploy a model that fails on real-world data.
+
+🤗 Datasets provides a `Dataset.train_test_split()` function that is based on the famous functionality from `scikit-learn`. Let's use it to split our training set into `train` and `validation` splits (we set the `seed` argument for reproducibility):
+
+```py
+drug_dataset_clean = drug_dataset["train"].train_test_split(train_size=0.8, seed=42)
+# Rename the default "test" split to "validation"
+drug_dataset_clean["validation"] = drug_dataset_clean.pop("test")
+# Add the "test" set to our `DatasetDict`
+drug_dataset_clean["test"] = drug_dataset["test"]
+drug_dataset_clean
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
+ num_rows: 110811
+ })
+ validation: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
+ num_rows: 27703
+ })
+ test: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length', 'review_clean'],
+ num_rows: 46108
+ })
+})
+```
+
+Great, we've now prepared a dataset that's ready for training some models on! In [section 5](/course/chapter5/5) we'll show you how to upload datasets to the Hugging Face Hub, but for now let's cap off our analysis by looking at a few ways you can save datasets on your local machine.
+
+## Saving a dataset[[saving-a-dataset]]
+
+
+
+Although 🤗 Datasets will cache every downloaded dataset and the operations performed on it, there are times when you'll want to save a dataset to disk (e.g., in case the cache gets deleted). As shown in the table below, 🤗 Datasets provides three main functions to save your dataset in different formats:
+
+| Data format | Function |
+| :---------: | :--------------------: |
+| Arrow | `Dataset.save_to_disk()` |
+| CSV | `Dataset.to_csv()` |
+| JSON | `Dataset.to_json()` |
+
+For example, let's save our cleaned dataset in the Arrow format:
+
+```py
+drug_dataset_clean.save_to_disk("drug-reviews")
+```
+
+This will create a directory with the following structure:
+
+```
+drug-reviews/
+├── dataset_dict.json
+├── test
+│ ├── dataset.arrow
+│ ├── dataset_info.json
+│ └── state.json
+├── train
+│ ├── dataset.arrow
+│ ├── dataset_info.json
+│ ├── indices.arrow
+│ └── state.json
+└── validation
+ ├── dataset.arrow
+ ├── dataset_info.json
+ ├── indices.arrow
+ └── state.json
+```
+
+where we can see that each split is associated with its own *dataset.arrow* table, and some metadata in *dataset_info.json* and *state.json*. You can think of the Arrow format as a fancy table of columns and rows that is optimized for building high-performance applications that process and transport large datasets.
+
+Once the dataset is saved, we can load it by using the `load_from_disk()` function as follows:
+
+```py
+from datasets import load_from_disk
+
+drug_dataset_reloaded = load_from_disk("drug-reviews")
+drug_dataset_reloaded
+```
+
+```python out
+DatasetDict({
+ train: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
+ num_rows: 110811
+ })
+ validation: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
+ num_rows: 27703
+ })
+ test: Dataset({
+ features: ['patient_id', 'drugName', 'condition', 'review', 'rating', 'date', 'usefulCount', 'review_length'],
+ num_rows: 46108
+ })
+})
+```
+
+For the CSV and JSON formats, we have to store each split as a separate file. One way to do this is by iterating over the keys and values in the `DatasetDict` object:
+
+```py
+for split, dataset in drug_dataset_clean.items():
+ dataset.to_json(f"drug-reviews-{split}.jsonl")
+```
+
+This saves each split in [JSON Lines format](https://jsonlines.org), where each row in the dataset is stored as a single line of JSON. Here's what the first example looks like:
+
+```py
+!head -n 1 drug-reviews-train.jsonl
+```
+
+```python out
+{"patient_id":141780,"drugName":"Escitalopram","condition":"depression","review":"\"I seemed to experience the regular side effects of LEXAPRO, insomnia, low sex drive, sleepiness during the day. I am taking it at night because my doctor said if it made me tired to take it at night. I assumed it would and started out taking it at night. Strange dreams, some pleasant. I was diagnosed with fibromyalgia. Seems to be helping with the pain. Have had anxiety and depression in my family, and have tried quite a few other medications that haven't worked. Only have been on it for two weeks but feel more positive in my mind, want to accomplish more in my life. Hopefully the side effects will dwindle away, worth it to stick with it from hearing others responses. Great medication.\"","rating":9.0,"date":"May 29, 2011","usefulCount":10,"review_length":125}
+```
+
+We can then use the techniques from [section 2](/course/chapter5/2) to load the JSON files as follows:
+
+```py
+data_files = {
+ "train": "drug-reviews-train.jsonl",
+ "validation": "drug-reviews-validation.jsonl",
+ "test": "drug-reviews-test.jsonl",
+}
+drug_dataset_reloaded = load_dataset("json", data_files=data_files)
+```
+
+And that's it for our excursion into data wrangling with 🤗 Datasets! Now that we have a cleaned dataset for training a model on, here are a few ideas that you could try out:
+
+1. Use the techniques from [Chapter 3](/course/chapter3) to train a classifier that can predict the patient condition based on the drug review.
+2. Use the `summarization` pipeline from [Chapter 1](/course/chapter1) to generate summaries of the reviews.
+
+Next, we'll take a look at how 🤗 Datasets can enable you to work with huge datasets without blowing up your laptop!
diff --git a/chapters/en/chapter5/4.mdx b/chapters/en/chapter5/4.mdx
index 1fdd4e031..d4e78e89c 100644
--- a/chapters/en/chapter5/4.mdx
+++ b/chapters/en/chapter5/4.mdx
@@ -1,272 +1,272 @@
-# Big data? 🤗 Datasets to the rescue![[big-data-datasets-to-the-rescue]]
-
-
-
-
-Nowadays it is not uncommon to find yourself working with multi-gigabyte datasets, especially if you're planning to pretrain a transformer like BERT or GPT-2 from scratch. In these cases, even _loading_ the data can be a challenge. For example, the WebText corpus used to pretrain GPT-2 consists of over 8 million documents and 40 GB of text -- loading this into your laptop's RAM is likely to give it a heart attack!
-
-Fortunately, 🤗 Datasets has been designed to overcome these limitations. It frees you from memory management problems by treating datasets as _memory-mapped_ files, and from hard drive limits by _streaming_ the entries in a corpus.
-
-
-
-In this section we'll explore these features of 🤗 Datasets with a huge 825 GB corpus known as [the Pile](https://pile.eleuther.ai). Let's get started!
-
-## What is the Pile?[[what-is-the-pile]]
-
-The Pile is an English text corpus that was created by [EleutherAI](https://www.eleuther.ai) for training large-scale language models. It includes a diverse range of datasets, spanning scientific articles, GitHub code repositories, and filtered web text. The training corpus is available in [14 GB chunks](https://the-eye.eu/public/AI/pile/), and you can also download several of the [individual components](https://the-eye.eu/public/AI/pile_preliminary_components/). Let's start by taking a look at the PubMed Abstracts dataset, which is a corpus of abstracts from 15 million biomedical publications on [PubMed](https://pubmed.ncbi.nlm.nih.gov/). The dataset is in [JSON Lines format](https://jsonlines.org) and is compressed using the `zstandard` library, so first we need to install that:
-
-```py
-!pip install zstandard
-```
-
-Next, we can load the dataset using the method for remote files that we learned in [section 2](/course/chapter5/2):
-
-```py
-from datasets import load_dataset
-
-# This takes a few minutes to run, so go grab a tea or coffee while you wait :)
-data_files = "https://the-eye.eu/public/AI/pile_preliminary_components/PUBMED_title_abstracts_2019_baseline.jsonl.zst"
-pubmed_dataset = load_dataset("json", data_files=data_files, split="train")
-pubmed_dataset
-```
-
-```python out
-Dataset({
- features: ['meta', 'text'],
- num_rows: 15518009
-})
-```
-
-We can see that there are 15,518,009 rows and 2 columns in our dataset -- that's a lot!
-
-> [!TIP]
-> ✎ By default, 🤗 Datasets will decompress the files needed to load a dataset. If you want to preserve hard drive space, you can pass `DownloadConfig(delete_extracted=True)` to the `download_config` argument of `load_dataset()`. See the [documentation](https://huggingface.co/docs/datasets/package_reference/builder_classes#datasets.DownloadConfig) for more details.
-
-Let's inspect the contents of the first example:
-
-```py
-pubmed_dataset[0]
-```
-
-```python out
-{'meta': {'pmid': 11409574, 'language': 'eng'},
- 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
-```
-
-Okay, this looks like the abstract from a medical article. Now let's see how much RAM we've used to load the dataset!
-
-## The magic of memory mapping[[the-magic-of-memory-mapping]]
-
-A simple way to measure memory usage in Python is with the [`psutil`](https://psutil.readthedocs.io/en/latest/) library, which can be installed with `pip` as follows:
-
-```python
-!pip install psutil
-```
-
-It provides a `Process` class that allows us to check the memory usage of the current process as follows:
-
-```py
-import psutil
-
-# Process.memory_info is expressed in bytes, so convert to megabytes
-print(f"RAM used: {psutil.Process().memory_info().rss / (1024 * 1024):.2f} MB")
-```
-
-```python out
-RAM used: 5678.33 MB
-```
-
-Here the `rss` attribute refers to the _resident set size_, which is the fraction of memory that a process occupies in RAM. This measurement also includes the memory used by the Python interpreter and the libraries we've loaded, so the actual amount of memory used to load the dataset is a bit smaller. For comparison, let's see how large the dataset is on disk, using the `dataset_size` attribute. Since the result is expressed in bytes like before, we need to manually convert it to gigabytes:
-
-```py
-print(f"Dataset size in bytes: {pubmed_dataset.dataset_size}")
-size_gb = pubmed_dataset.dataset_size / (1024**3)
-print(f"Dataset size (cache file) : {size_gb:.2f} GB")
-```
-
-```python out
-Dataset size in bytes : 20979437051
-Dataset size (cache file) : 19.54 GB
-```
-
-Nice -- despite it being almost 20 GB large, we're able to load and access the dataset with much less RAM!
-
-> [!TIP]
-> ✏️ **Try it out!** Pick one of the [subsets](https://the-eye.eu/public/AI/pile_preliminary_components/) from the Pile that is larger than your laptop or desktop's RAM, load it with 🤗 Datasets, and measure the amount of RAM used. Note that to get an accurate measurement, you'll want to do this in a new process. You can find the decompressed sizes of each subset in Table 1 of [the Pile paper](https://arxiv.org/abs/2101.00027).
-
-If you're familiar with Pandas, this result might come as a surprise because of Wes Kinney's famous [rule of thumb](https://wesmckinney.com/blog/apache-arrow-pandas-internals/) that you typically need 5 to 10 times as much RAM as the size of your dataset. So how does 🤗 Datasets solve this memory management problem? 🤗 Datasets treats each dataset as a [memory-mapped file](https://en.wikipedia.org/wiki/Memory-mapped_file), which provides a mapping between RAM and filesystem storage that allows the library to access and operate on elements of the dataset without needing to fully load it into memory.
-
-Memory-mapped files can also be shared across multiple processes, which enables methods like `Dataset.map()` to be parallelized without needing to move or copy the dataset. Under the hood, these capabilities are all realized by the [Apache Arrow](https://arrow.apache.org) memory format and [`pyarrow`](https://arrow.apache.org/docs/python/index.html) library, which make the data loading and processing lightning fast. (For more details about Apache Arrow and comparisons to Pandas, check out [Dejan Simic's blog post](https://towardsdatascience.com/apache-arrow-read-dataframe-with-zero-memory-69634092b1a).) To see this in action, let's run a little speed test by iterating over all the elements in the PubMed Abstracts dataset:
-
-```py
-import timeit
-
-code_snippet = """batch_size = 1000
-
-for idx in range(0, len(pubmed_dataset), batch_size):
- _ = pubmed_dataset[idx:idx + batch_size]
-"""
-
-time = timeit.timeit(stmt=code_snippet, number=1, globals=globals())
-print(
- f"Iterated over {len(pubmed_dataset)} examples (about {size_gb:.1f} GB) in "
- f"{time:.1f}s, i.e. {size_gb/time:.3f} GB/s"
-)
-```
-
-```python out
-'Iterated over 15518009 examples (about 19.5 GB) in 64.2s, i.e. 0.304 GB/s'
-```
-
-Here we've used Python's `timeit` module to measure the execution time taken by `code_snippet`. You'll typically be able to iterate over a dataset at speed of a few tenths of a GB/s to several GB/s. This works great for the vast majority of applications, but sometimes you'll have to work with a dataset that is too large to even store on your laptop's hard drive. For example, if we tried to download the Pile in its entirety, we'd need 825 GB of free disk space! To handle these cases, 🤗 Datasets provides a streaming feature that allows us to download and access elements on the fly, without needing to download the whole dataset. Let's take a look at how this works.
-
-> [!TIP]
-> 💡 In Jupyter notebooks you can also time cells using the [`%%timeit` magic function](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit).
-
-## Streaming datasets[[streaming-datasets]]
-
-To enable dataset streaming you just need to pass the `streaming=True` argument to the `load_dataset()` function. For example, let's load the PubMed Abstracts dataset again, but in streaming mode:
-
-```py
-pubmed_dataset_streamed = load_dataset(
- "json", data_files=data_files, split="train", streaming=True
-)
-```
-
-Instead of the familiar `Dataset` that we've encountered elsewhere in this chapter, the object returned with `streaming=True` is an `IterableDataset`. As the name suggests, to access the elements of an `IterableDataset` we need to iterate over it. We can access the first element of our streamed dataset as follows:
-
-
-```py
-next(iter(pubmed_dataset_streamed))
-```
-
-```python out
-{'meta': {'pmid': 11409574, 'language': 'eng'},
- 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
-```
-
-The elements from a streamed dataset can be processed on the fly using `IterableDataset.map()`, which is useful during training if you need to tokenize the inputs. The process is exactly the same as the one we used to tokenize our dataset in [Chapter 3](/course/chapter3), with the only difference being that outputs are returned one by one:
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
-tokenized_dataset = pubmed_dataset_streamed.map(lambda x: tokenizer(x["text"]))
-next(iter(tokenized_dataset))
-```
-
-```python out
-{'input_ids': [101, 4958, 5178, 4328, 6779, ...], 'attention_mask': [1, 1, 1, 1, 1, ...]}
-```
-
-> [!TIP]
-> 💡 To speed up tokenization with streaming you can pass `batched=True`, as we saw in the last section. It will process the examples batch by batch; the default batch size is 1,000 and can be specified with the `batch_size` argument.
-
-You can also shuffle a streamed dataset using `IterableDataset.shuffle()`, but unlike `Dataset.shuffle()` this only shuffles the elements in a predefined `buffer_size`:
-
-```py
-shuffled_dataset = pubmed_dataset_streamed.shuffle(buffer_size=10_000, seed=42)
-next(iter(shuffled_dataset))
-```
-
-```python out
-{'meta': {'pmid': 11410799, 'language': 'eng'},
- 'text': 'Randomized study of dose or schedule modification of granulocyte colony-stimulating factor in platinum-based chemotherapy for elderly patients with lung cancer ...'}
-```
-
-In this example, we selected a random example from the first 10,000 examples in the buffer. Once an example is accessed, its spot in the buffer is filled with the next example in the corpus (i.e., the 10,001st example in the case above). You can also select elements from a streamed dataset using the `IterableDataset.take()` and `IterableDataset.skip()` functions, which act in a similar way to `Dataset.select()`. For example, to select the first 5 examples in the PubMed Abstracts dataset we can do the following:
-
-```py
-dataset_head = pubmed_dataset_streamed.take(5)
-list(dataset_head)
-```
-
-```python out
-[{'meta': {'pmid': 11409574, 'language': 'eng'},
- 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
- {'meta': {'pmid': 11409575, 'language': 'eng'},
- 'text': 'Clinical signs of hypoxaemia in children with acute lower respiratory infection: indicators of oxygen therapy ...'},
- {'meta': {'pmid': 11409576, 'language': 'eng'},
- 'text': "Hypoxaemia in children with severe pneumonia in Papua New Guinea ..."},
- {'meta': {'pmid': 11409577, 'language': 'eng'},
- 'text': 'Oxygen concentrators and cylinders ...'},
- {'meta': {'pmid': 11409578, 'language': 'eng'},
- 'text': 'Oxygen supply in rural africa: a personal experience ...'}]
-```
-
-Similarly, you can use the `IterableDataset.skip()` function to create training and validation splits from a shuffled dataset as follows:
-
-```py
-# Skip the first 1,000 examples and include the rest in the training set
-train_dataset = shuffled_dataset.skip(1000)
-# Take the first 1,000 examples for the validation set
-validation_dataset = shuffled_dataset.take(1000)
-```
-
-Let's round out our exploration of dataset streaming with a common application: combining multiple datasets together to create a single corpus. 🤗 Datasets provides an `interleave_datasets()` function that converts a list of `IterableDataset` objects into a single `IterableDataset`, where the elements of the new dataset are obtained by alternating among the source examples. This function is especially useful when you're trying to combine large datasets, so as an example let's stream the FreeLaw subset of the Pile, which is a 51 GB dataset of legal opinions from US courts:
-
-```py
-law_dataset_streamed = load_dataset(
- "json",
- data_files="https://the-eye.eu/public/AI/pile_preliminary_components/FreeLaw_Opinions.jsonl.zst",
- split="train",
- streaming=True,
-)
-next(iter(law_dataset_streamed))
-```
-
-```python out
-{'meta': {'case_ID': '110921.json',
- 'case_jurisdiction': 'scotus.tar.gz',
- 'date_created': '2010-04-28T17:12:49Z'},
- 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}
-```
-
-This dataset is large enough to stress the RAM of most laptops, yet we've been able to load and access it without breaking a sweat! Let's now combine the examples from the FreeLaw and PubMed Abstracts datasets with the `interleave_datasets()` function:
-
-```py
-from itertools import islice
-from datasets import interleave_datasets
-
-combined_dataset = interleave_datasets([pubmed_dataset_streamed, law_dataset_streamed])
-list(islice(combined_dataset, 2))
-```
-
-```python out
-[{'meta': {'pmid': 11409574, 'language': 'eng'},
- 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
- {'meta': {'case_ID': '110921.json',
- 'case_jurisdiction': 'scotus.tar.gz',
- 'date_created': '2010-04-28T17:12:49Z'},
- 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}]
-```
-
-Here we've used the `islice()` function from Python's `itertools` module to select the first two examples from the combined dataset, and we can see that they match the first examples from each of the two source datasets.
-
-Finally, if you want to stream the Pile in its 825 GB entirety, you can grab all the prepared files as follows:
-
-```py
-base_url = "https://the-eye.eu/public/AI/pile/"
-data_files = {
- "train": [base_url + "train/" + f"{idx:02d}.jsonl.zst" for idx in range(30)],
- "validation": base_url + "val.jsonl.zst",
- "test": base_url + "test.jsonl.zst",
-}
-pile_dataset = load_dataset("json", data_files=data_files, streaming=True)
-next(iter(pile_dataset["train"]))
-```
-
-```python out
-{'meta': {'pile_set_name': 'Pile-CC'},
- 'text': 'It is done, and submitted. You can play “Survival of the Tastiest” on Android, and on the web...'}
-```
-
-> [!TIP]
-> ✏️ **Try it out!** Use one of the large Common Crawl corpora like [`mc4`](https://huggingface.co/datasets/mc4) or [`oscar`](https://huggingface.co/datasets/oscar) to create a streaming multilingual dataset that represents the spoken proportions of languages in a country of your choice. For example, the four national languages in Switzerland are German, French, Italian, and Romansh, so you could try creating a Swiss corpus by sampling the Oscar subsets according to their spoken proportion.
-
-You now have all the tools you need to load and process datasets of all shapes and sizes -- but unless you're exceptionally lucky, there will come a point in your NLP journey where you'll have to actually create a dataset to solve the problem at hand. That's the topic of the next section!
+# Big data? 🤗 Datasets to the rescue![[big-data-datasets-to-the-rescue]]
+
+
+
+
+Nowadays it is not uncommon to find yourself working with multi-gigabyte datasets, especially if you're planning to pretrain a transformer like BERT or GPT-2 from scratch. In these cases, even _loading_ the data can be a challenge. For example, the WebText corpus used to pretrain GPT-2 consists of over 8 million documents and 40 GB of text -- loading this into your laptop's RAM is likely to give it a heart attack!
+
+Fortunately, 🤗 Datasets has been designed to overcome these limitations. It frees you from memory management problems by treating datasets as _memory-mapped_ files, and from hard drive limits by _streaming_ the entries in a corpus.
+
+
+
+In this section we'll explore these features of 🤗 Datasets with a huge 825 GB corpus known as [the Pile](https://pile.eleuther.ai). Let's get started!
+
+## What is the Pile?[[what-is-the-pile]]
+
+The Pile is an English text corpus that was created by [EleutherAI](https://www.eleuther.ai) for training large-scale language models. It includes a diverse range of datasets, spanning scientific articles, GitHub code repositories, and filtered web text. The training corpus is available in [14 GB chunks](https://the-eye.eu/public/AI/pile/), and you can also download several of the [individual components](https://the-eye.eu/public/AI/pile_preliminary_components/). Let's start by taking a look at the PubMed Abstracts dataset, which is a corpus of abstracts from 15 million biomedical publications on [PubMed](https://pubmed.ncbi.nlm.nih.gov/). The dataset is in [JSON Lines format](https://jsonlines.org) and is compressed using the `zstandard` library, so first we need to install that:
+
+```py
+!pip install zstandard
+```
+
+Next, we can load the dataset using the method for remote files that we learned in [section 2](/course/chapter5/2):
+
+```py
+from datasets import load_dataset
+
+# This takes a few minutes to run, so go grab a tea or coffee while you wait :)
+data_files = "https://the-eye.eu/public/AI/pile_preliminary_components/PUBMED_title_abstracts_2019_baseline.jsonl.zst"
+pubmed_dataset = load_dataset("json", data_files=data_files, split="train")
+pubmed_dataset
+```
+
+```python out
+Dataset({
+ features: ['meta', 'text'],
+ num_rows: 15518009
+})
+```
+
+We can see that there are 15,518,009 rows and 2 columns in our dataset -- that's a lot!
+
+> [!TIP]
+> ✎ By default, 🤗 Datasets will decompress the files needed to load a dataset. If you want to preserve hard drive space, you can pass `DownloadConfig(delete_extracted=True)` to the `download_config` argument of `load_dataset()`. See the [documentation](https://huggingface.co/docs/datasets/package_reference/builder_classes#datasets.DownloadConfig) for more details.
+
+Let's inspect the contents of the first example:
+
+```py
+pubmed_dataset[0]
+```
+
+```python out
+{'meta': {'pmid': 11409574, 'language': 'eng'},
+ 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
+```
+
+Okay, this looks like the abstract from a medical article. Now let's see how much RAM we've used to load the dataset!
+
+## The magic of memory mapping[[the-magic-of-memory-mapping]]
+
+A simple way to measure memory usage in Python is with the [`psutil`](https://psutil.readthedocs.io/en/latest/) library, which can be installed with `pip` as follows:
+
+```python
+!pip install psutil
+```
+
+It provides a `Process` class that allows us to check the memory usage of the current process as follows:
+
+```py
+import psutil
+
+# Process.memory_info is expressed in bytes, so convert to megabytes
+print(f"RAM used: {psutil.Process().memory_info().rss / (1024 * 1024):.2f} MB")
+```
+
+```python out
+RAM used: 5678.33 MB
+```
+
+Here the `rss` attribute refers to the _resident set size_, which is the fraction of memory that a process occupies in RAM. This measurement also includes the memory used by the Python interpreter and the libraries we've loaded, so the actual amount of memory used to load the dataset is a bit smaller. For comparison, let's see how large the dataset is on disk, using the `dataset_size` attribute. Since the result is expressed in bytes like before, we need to manually convert it to gigabytes:
+
+```py
+print(f"Dataset size in bytes: {pubmed_dataset.dataset_size}")
+size_gb = pubmed_dataset.dataset_size / (1024**3)
+print(f"Dataset size (cache file) : {size_gb:.2f} GB")
+```
+
+```python out
+Dataset size in bytes : 20979437051
+Dataset size (cache file) : 19.54 GB
+```
+
+Nice -- despite it being almost 20 GB large, we're able to load and access the dataset with much less RAM!
+
+> [!TIP]
+> ✏️ **Try it out!** Pick one of the [subsets](https://the-eye.eu/public/AI/pile_preliminary_components/) from the Pile that is larger than your laptop or desktop's RAM, load it with 🤗 Datasets, and measure the amount of RAM used. Note that to get an accurate measurement, you'll want to do this in a new process. You can find the decompressed sizes of each subset in Table 1 of [the Pile paper](https://arxiv.org/abs/2101.00027).
+
+If you're familiar with Pandas, this result might come as a surprise because of Wes Kinney's famous [rule of thumb](https://wesmckinney.com/blog/apache-arrow-pandas-internals/) that you typically need 5 to 10 times as much RAM as the size of your dataset. So how does 🤗 Datasets solve this memory management problem? 🤗 Datasets treats each dataset as a [memory-mapped file](https://en.wikipedia.org/wiki/Memory-mapped_file), which provides a mapping between RAM and filesystem storage that allows the library to access and operate on elements of the dataset without needing to fully load it into memory.
+
+Memory-mapped files can also be shared across multiple processes, which enables methods like `Dataset.map()` to be parallelized without needing to move or copy the dataset. Under the hood, these capabilities are all realized by the [Apache Arrow](https://arrow.apache.org) memory format and [`pyarrow`](https://arrow.apache.org/docs/python/index.html) library, which make the data loading and processing lightning fast. (For more details about Apache Arrow and comparisons to Pandas, check out [Dejan Simic's blog post](https://towardsdatascience.com/apache-arrow-read-dataframe-with-zero-memory-69634092b1a).) To see this in action, let's run a little speed test by iterating over all the elements in the PubMed Abstracts dataset:
+
+```py
+import timeit
+
+code_snippet = """batch_size = 1000
+
+for idx in range(0, len(pubmed_dataset), batch_size):
+ _ = pubmed_dataset[idx:idx + batch_size]
+"""
+
+time = timeit.timeit(stmt=code_snippet, number=1, globals=globals())
+print(
+ f"Iterated over {len(pubmed_dataset)} examples (about {size_gb:.1f} GB) in "
+ f"{time:.1f}s, i.e. {size_gb/time:.3f} GB/s"
+)
+```
+
+```python out
+'Iterated over 15518009 examples (about 19.5 GB) in 64.2s, i.e. 0.304 GB/s'
+```
+
+Here we've used Python's `timeit` module to measure the execution time taken by `code_snippet`. You'll typically be able to iterate over a dataset at speed of a few tenths of a GB/s to several GB/s. This works great for the vast majority of applications, but sometimes you'll have to work with a dataset that is too large to even store on your laptop's hard drive. For example, if we tried to download the Pile in its entirety, we'd need 825 GB of free disk space! To handle these cases, 🤗 Datasets provides a streaming feature that allows us to download and access elements on the fly, without needing to download the whole dataset. Let's take a look at how this works.
+
+> [!TIP]
+> 💡 In Jupyter notebooks you can also time cells using the [`%%timeit` magic function](https://ipython.readthedocs.io/en/stable/interactive/magics.html#magic-timeit).
+
+## Streaming datasets[[streaming-datasets]]
+
+To enable dataset streaming you just need to pass the `streaming=True` argument to the `load_dataset()` function. For example, let's load the PubMed Abstracts dataset again, but in streaming mode:
+
+```py
+pubmed_dataset_streamed = load_dataset(
+ "json", data_files=data_files, split="train", streaming=True
+)
+```
+
+Instead of the familiar `Dataset` that we've encountered elsewhere in this chapter, the object returned with `streaming=True` is an `IterableDataset`. As the name suggests, to access the elements of an `IterableDataset` we need to iterate over it. We can access the first element of our streamed dataset as follows:
+
+
+```py
+next(iter(pubmed_dataset_streamed))
+```
+
+```python out
+{'meta': {'pmid': 11409574, 'language': 'eng'},
+ 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection.\nTo determine the prevalence of hypoxaemia in children aged under 5 years suffering acute lower respiratory infections (ALRI), the risk factors for hypoxaemia in children under 5 years of age with ALRI, and the association of hypoxaemia with an increased risk of dying in children of the same age ...'}
+```
+
+The elements from a streamed dataset can be processed on the fly using `IterableDataset.map()`, which is useful during training if you need to tokenize the inputs. The process is exactly the same as the one we used to tokenize our dataset in [Chapter 3](/course/chapter3), with the only difference being that outputs are returned one by one:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("distilbert-base-uncased")
+tokenized_dataset = pubmed_dataset_streamed.map(lambda x: tokenizer(x["text"]))
+next(iter(tokenized_dataset))
+```
+
+```python out
+{'input_ids': [101, 4958, 5178, 4328, 6779, ...], 'attention_mask': [1, 1, 1, 1, 1, ...]}
+```
+
+> [!TIP]
+> 💡 To speed up tokenization with streaming you can pass `batched=True`, as we saw in the last section. It will process the examples batch by batch; the default batch size is 1,000 and can be specified with the `batch_size` argument.
+
+You can also shuffle a streamed dataset using `IterableDataset.shuffle()`, but unlike `Dataset.shuffle()` this only shuffles the elements in a predefined `buffer_size`:
+
+```py
+shuffled_dataset = pubmed_dataset_streamed.shuffle(buffer_size=10_000, seed=42)
+next(iter(shuffled_dataset))
+```
+
+```python out
+{'meta': {'pmid': 11410799, 'language': 'eng'},
+ 'text': 'Randomized study of dose or schedule modification of granulocyte colony-stimulating factor in platinum-based chemotherapy for elderly patients with lung cancer ...'}
+```
+
+In this example, we selected a random example from the first 10,000 examples in the buffer. Once an example is accessed, its spot in the buffer is filled with the next example in the corpus (i.e., the 10,001st example in the case above). You can also select elements from a streamed dataset using the `IterableDataset.take()` and `IterableDataset.skip()` functions, which act in a similar way to `Dataset.select()`. For example, to select the first 5 examples in the PubMed Abstracts dataset we can do the following:
+
+```py
+dataset_head = pubmed_dataset_streamed.take(5)
+list(dataset_head)
+```
+
+```python out
+[{'meta': {'pmid': 11409574, 'language': 'eng'},
+ 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
+ {'meta': {'pmid': 11409575, 'language': 'eng'},
+ 'text': 'Clinical signs of hypoxaemia in children with acute lower respiratory infection: indicators of oxygen therapy ...'},
+ {'meta': {'pmid': 11409576, 'language': 'eng'},
+ 'text': "Hypoxaemia in children with severe pneumonia in Papua New Guinea ..."},
+ {'meta': {'pmid': 11409577, 'language': 'eng'},
+ 'text': 'Oxygen concentrators and cylinders ...'},
+ {'meta': {'pmid': 11409578, 'language': 'eng'},
+ 'text': 'Oxygen supply in rural africa: a personal experience ...'}]
+```
+
+Similarly, you can use the `IterableDataset.skip()` function to create training and validation splits from a shuffled dataset as follows:
+
+```py
+# Skip the first 1,000 examples and include the rest in the training set
+train_dataset = shuffled_dataset.skip(1000)
+# Take the first 1,000 examples for the validation set
+validation_dataset = shuffled_dataset.take(1000)
+```
+
+Let's round out our exploration of dataset streaming with a common application: combining multiple datasets together to create a single corpus. 🤗 Datasets provides an `interleave_datasets()` function that converts a list of `IterableDataset` objects into a single `IterableDataset`, where the elements of the new dataset are obtained by alternating among the source examples. This function is especially useful when you're trying to combine large datasets, so as an example let's stream the FreeLaw subset of the Pile, which is a 51 GB dataset of legal opinions from US courts:
+
+```py
+law_dataset_streamed = load_dataset(
+ "json",
+ data_files="https://the-eye.eu/public/AI/pile_preliminary_components/FreeLaw_Opinions.jsonl.zst",
+ split="train",
+ streaming=True,
+)
+next(iter(law_dataset_streamed))
+```
+
+```python out
+{'meta': {'case_ID': '110921.json',
+ 'case_jurisdiction': 'scotus.tar.gz',
+ 'date_created': '2010-04-28T17:12:49Z'},
+ 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}
+```
+
+This dataset is large enough to stress the RAM of most laptops, yet we've been able to load and access it without breaking a sweat! Let's now combine the examples from the FreeLaw and PubMed Abstracts datasets with the `interleave_datasets()` function:
+
+```py
+from itertools import islice
+from datasets import interleave_datasets
+
+combined_dataset = interleave_datasets([pubmed_dataset_streamed, law_dataset_streamed])
+list(islice(combined_dataset, 2))
+```
+
+```python out
+[{'meta': {'pmid': 11409574, 'language': 'eng'},
+ 'text': 'Epidemiology of hypoxaemia in children with acute lower respiratory infection ...'},
+ {'meta': {'case_ID': '110921.json',
+ 'case_jurisdiction': 'scotus.tar.gz',
+ 'date_created': '2010-04-28T17:12:49Z'},
+ 'text': '\n461 U.S. 238 (1983)\nOLIM ET AL.\nv.\nWAKINEKONA\nNo. 81-1581.\nSupreme Court of United States.\nArgued January 19, 1983.\nDecided April 26, 1983.\nCERTIORARI TO THE UNITED STATES COURT OF APPEALS FOR THE NINTH CIRCUIT\n*239 Michael A. Lilly, First Deputy Attorney General of Hawaii, argued the cause for petitioners. With him on the brief was James H. Dannenberg, Deputy Attorney General...'}]
+```
+
+Here we've used the `islice()` function from Python's `itertools` module to select the first two examples from the combined dataset, and we can see that they match the first examples from each of the two source datasets.
+
+Finally, if you want to stream the Pile in its 825 GB entirety, you can grab all the prepared files as follows:
+
+```py
+base_url = "https://the-eye.eu/public/AI/pile/"
+data_files = {
+ "train": [base_url + "train/" + f"{idx:02d}.jsonl.zst" for idx in range(30)],
+ "validation": base_url + "val.jsonl.zst",
+ "test": base_url + "test.jsonl.zst",
+}
+pile_dataset = load_dataset("json", data_files=data_files, streaming=True)
+next(iter(pile_dataset["train"]))
+```
+
+```python out
+{'meta': {'pile_set_name': 'Pile-CC'},
+ 'text': 'It is done, and submitted. You can play “Survival of the Tastiest” on Android, and on the web...'}
+```
+
+> [!TIP]
+> ✏️ **Try it out!** Use one of the large Common Crawl corpora like [`mc4`](https://huggingface.co/datasets/mc4) or [`oscar`](https://huggingface.co/datasets/oscar) to create a streaming multilingual dataset that represents the spoken proportions of languages in a country of your choice. For example, the four national languages in Switzerland are German, French, Italian, and Romansh, so you could try creating a Swiss corpus by sampling the Oscar subsets according to their spoken proportion.
+
+You now have all the tools you need to load and process datasets of all shapes and sizes -- but unless you're exceptionally lucky, there will come a point in your NLP journey where you'll have to actually create a dataset to solve the problem at hand. That's the topic of the next section!
diff --git a/chapters/en/chapter5/5.mdx b/chapters/en/chapter5/5.mdx
index aae856d22..70146aebd 100644
--- a/chapters/en/chapter5/5.mdx
+++ b/chapters/en/chapter5/5.mdx
@@ -1,388 +1,388 @@
-# Creating your own dataset[[creating-your-own-dataset]]
-
-
-
-Sometimes the dataset that you need to build an NLP application doesn't exist, so you'll need to create it yourself. In this section we'll show you how to create a corpus of [GitHub issues](https://github.com/features/issues/), which are commonly used to track bugs or features in GitHub repositories. This corpus could be used for various purposes, including:
-
-* Exploring how long it takes to close open issues or pull requests
-* Training a _multilabel classifier_ that can tag issues with metadata based on the issue's description (e.g., "bug," "enhancement," or "question")
-* Creating a semantic search engine to find which issues match a user's query
-
-Here we'll focus on creating the corpus, and in the next section we'll tackle the semantic search application. To keep things meta, we'll use the GitHub issues associated with a popular open source project: 🤗 Datasets! Let's take a look at how to get the data and explore the information contained in these issues.
-
-## Getting the data[[getting-the-data]]
-
-You can find all the issues in 🤗 Datasets by navigating to the repository's [Issues tab](https://github.com/huggingface/datasets/issues). As shown in the following screenshot, at the time of writing there were 331 open issues and 668 closed ones.
-
-
-

-
-
-If you click on one of these issues you'll find it contains a title, a description, and a set of labels that characterize the issue. An example is shown in the screenshot below.
-
-
-

-
-
-To download all the repository's issues, we'll use the [GitHub REST API](https://docs.github.com/en/rest) to poll the [`Issues` endpoint](https://docs.github.com/en/rest/reference/issues#list-repository-issues). This endpoint returns a list of JSON objects, with each object containing a large number of fields that include the title and description as well as metadata about the status of the issue and so on.
-
-A convenient way to download the issues is via the `requests` library, which is the standard way for making HTTP requests in Python. You can install the library by running:
-
-```python
-!pip install requests
-```
-
-Once the library is installed, you can make GET requests to the `Issues` endpoint by invoking the `requests.get()` function. For example, you can run the following command to retrieve the first issue on the first page:
-
-```py
-import requests
-
-url = "https://api.github.com/repos/huggingface/datasets/issues?page=1&per_page=1"
-response = requests.get(url)
-```
-
-The `response` object contains a lot of useful information about the request, including the HTTP status code:
-
-```py
-response.status_code
-```
-
-```python out
-200
-```
-
-where a `200` status means the request was successful (you can find a list of possible HTTP status codes [here](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)). What we are really interested in, though, is the _payload_, which can be accessed in various formats like bytes, strings, or JSON. Since we know our issues are in JSON format, let's inspect the payload as follows:
-
-```py
-response.json()
-```
-
-```python out
-[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
- 'repository_url': 'https://api.github.com/repos/huggingface/datasets',
- 'labels_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/labels{/name}',
- 'comments_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/comments',
- 'events_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/events',
- 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
- 'id': 968650274,
- 'node_id': 'MDExOlB1bGxSZXF1ZXN0NzEwNzUyMjc0',
- 'number': 2792,
- 'title': 'Update GooAQ',
- 'user': {'login': 'bhavitvyamalik',
- 'id': 19718818,
- 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
- 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
- 'gravatar_id': '',
- 'url': 'https://api.github.com/users/bhavitvyamalik',
- 'html_url': 'https://github.com/bhavitvyamalik',
- 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
- 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
- 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
- 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
- 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
- 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
- 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
- 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
- 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
- 'type': 'User',
- 'site_admin': False},
- 'labels': [],
- 'state': 'open',
- 'locked': False,
- 'assignee': None,
- 'assignees': [],
- 'milestone': None,
- 'comments': 1,
- 'created_at': '2021-08-12T11:40:18Z',
- 'updated_at': '2021-08-12T12:31:17Z',
- 'closed_at': None,
- 'author_association': 'CONTRIBUTOR',
- 'active_lock_reason': None,
- 'pull_request': {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/2792',
- 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
- 'diff_url': 'https://github.com/huggingface/datasets/pull/2792.diff',
- 'patch_url': 'https://github.com/huggingface/datasets/pull/2792.patch'},
- 'body': '[GooAQ](https://github.com/allenai/gooaq) dataset was recently updated after splits were added for the same. This PR contains new updated GooAQ with train/val/test splits and updated README as well.',
- 'performed_via_github_app': None}]
-```
-
-Whoa, that's a lot of information! We can see useful fields like `title`, `body`, and `number` that describe the issue, as well as information about the GitHub user who opened the issue.
-
-> [!TIP]
-> ✏️ **Try it out!** Click on a few of the URLs in the JSON payload above to get a feel for what type of information each GitHub issue is linked to.
-
-As described in the GitHub [documentation](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting), unauthenticated requests are limited to 60 requests per hour. Although you can increase the `per_page` query parameter to reduce the number of requests you make, you will still hit the rate limit on any repository that has more than a few thousand issues. So instead, you should follow GitHub's [instructions](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) on creating a _personal access token_ so that you can boost the rate limit to 5,000 requests per hour. Once you have your token, you can include it as part of the request header:
-
-```py
-GITHUB_TOKEN = xxx # Copy your GitHub token here
-headers = {"Authorization": f"token {GITHUB_TOKEN}"}
-```
-
-> [!WARNING]
-> ⚠️ Do not share a notebook with your `GITHUB_TOKEN` pasted in it. We recommend you delete the last cell once you have executed it to avoid leaking this information accidentally. Even better, store the token in a *.env* file and use the [`python-dotenv` library](https://github.com/theskumar/python-dotenv) to load it automatically for you as an environment variable.
-
-Now that we have our access token, let's create a function that can download all the issues from a GitHub repository:
-
-```py
-import time
-import math
-from pathlib import Path
-import pandas as pd
-from tqdm.notebook import tqdm
-
-
-def fetch_issues(
- owner="huggingface",
- repo="datasets",
- num_issues=10_000,
- rate_limit=5_000,
- issues_path=Path("."),
-):
- if not issues_path.is_dir():
- issues_path.mkdir(exist_ok=True)
-
- batch = []
- all_issues = []
- per_page = 100 # Number of issues to return per page
- num_pages = math.ceil(num_issues / per_page)
- base_url = "https://api.github.com/repos"
-
- for page in tqdm(range(num_pages)):
- # Query with state=all to get both open and closed issues
- query = f"issues?page={page}&per_page={per_page}&state=all"
- issues = requests.get(f"{base_url}/{owner}/{repo}/{query}", headers=headers)
- batch.extend(issues.json())
-
- if len(batch) > rate_limit and len(all_issues) < num_issues:
- all_issues.extend(batch)
- batch = [] # Flush batch for next time period
- print(f"Reached GitHub rate limit. Sleeping for one hour ...")
- time.sleep(60 * 60 + 1)
-
- all_issues.extend(batch)
- df = pd.DataFrame.from_records(all_issues)
- df.to_json(f"{issues_path}/{repo}-issues.jsonl", orient="records", lines=True)
- print(
- f"Downloaded all the issues for {repo}! Dataset stored at {issues_path}/{repo}-issues.jsonl"
- )
-```
-
-Now when we call `fetch_issues()` it will download all the issues in batches to avoid exceeding GitHub's limit on the number of requests per hour; the result will be stored in a _repository_name-issues.jsonl_ file, where each line is a JSON object the represents an issue. Let's use this function to grab all the issues from 🤗 Datasets:
-
-```py
-# Depending on your internet connection, this can take several minutes to run...
-fetch_issues()
-```
-
-Once the issues are downloaded we can load them locally using our newfound skills from [section 2](/course/chapter5/2):
-
-```py
-issues_dataset = load_dataset("json", data_files="datasets-issues.jsonl", split="train")
-issues_dataset
-```
-
-```python out
-Dataset({
- features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'timeline_url', 'performed_via_github_app'],
- num_rows: 3019
-})
-```
-
-Great, we've created our first dataset from scratch! But why are there several thousand issues when the [Issues tab](https://github.com/huggingface/datasets/issues) of the 🤗 Datasets repository only shows around 1,000 issues in total 🤔? As described in the GitHub [documentation](https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user), that's because we've downloaded all the pull requests as well:
-
-> GitHub's REST API v3 considers every pull request an issue, but not every issue is a pull request. For this reason, "Issues" endpoints may return both issues and pull requests in the response. You can identify pull requests by the `pull_request` key. Be aware that the `id` of a pull request returned from "Issues" endpoints will be an issue id.
-
-Since the contents of issues and pull requests are quite different, let's do some minor preprocessing to enable us to distinguish between them.
-
-## Cleaning up the data[[cleaning-up-the-data]]
-
-The above snippet from GitHub's documentation tells us that the `pull_request` column can be used to differentiate between issues and pull requests. Let's look at a random sample to see what the difference is. As we did in [section 3](/course/chapter5/3), we'll chain `Dataset.shuffle()` and `Dataset.select()` to create a random sample and then zip the `html_url` and `pull_request` columns so we can compare the various URLs:
-
-```py
-sample = issues_dataset.shuffle(seed=666).select(range(3))
-
-# Print out the URL and pull request entries
-for url, pr in zip(sample["html_url"], sample["pull_request"]):
- print(f">> URL: {url}")
- print(f">> Pull request: {pr}\n")
-```
-
-```python out
->> URL: https://github.com/huggingface/datasets/pull/850
->> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/850', 'html_url': 'https://github.com/huggingface/datasets/pull/850', 'diff_url': 'https://github.com/huggingface/datasets/pull/850.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/850.patch'}
-
->> URL: https://github.com/huggingface/datasets/issues/2773
->> Pull request: None
-
->> URL: https://github.com/huggingface/datasets/pull/783
->> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/783', 'html_url': 'https://github.com/huggingface/datasets/pull/783', 'diff_url': 'https://github.com/huggingface/datasets/pull/783.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/783.patch'}
-```
-
-Here we can see that each pull request is associated with various URLs, while ordinary issues have a `None` entry. We can use this distinction to create a new `is_pull_request` column that checks whether the `pull_request` field is `None` or not:
-
-```py
-issues_dataset = issues_dataset.map(
- lambda x: {"is_pull_request": False if x["pull_request"] is None else True}
-)
-```
-
-> [!TIP]
-> ✏️ **Try it out!** Calculate the average time it takes to close issues in 🤗 Datasets. You may find the `Dataset.filter()` function useful to filter out the pull requests and open issues, and you can use the `Dataset.set_format()` function to convert the dataset to a `DataFrame` so you can easily manipulate the `created_at` and `closed_at` timestamps. For bonus points, calculate the average time it takes to close pull requests.
-
-Although we could proceed to further clean up the dataset by dropping or renaming some columns, it is generally a good practice to keep the dataset as "raw" as possible at this stage so that it can be easily used in multiple applications.
-
-Before we push our dataset to the Hugging Face Hub, let's deal with one thing that's missing from it: the comments associated with each issue and pull request. We'll add them next with -- you guessed it -- the GitHub REST API!
-
-## Augmenting the dataset[[augmenting-the-dataset]]
-
-As shown in the following screenshot, the comments associated with an issue or pull request provide a rich source of information, especially if we're interested in building a search engine to answer user queries about the library.
-
-
-

-
-
-The GitHub REST API provides a [`Comments` endpoint](https://docs.github.com/en/rest/reference/issues#list-issue-comments) that returns all the comments associated with an issue number. Let's test the endpoint to see what it returns:
-
-```py
-issue_number = 2792
-url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
-response = requests.get(url, headers=headers)
-response.json()
-```
-
-```python out
-[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/comments/897594128',
- 'html_url': 'https://github.com/huggingface/datasets/pull/2792#issuecomment-897594128',
- 'issue_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
- 'id': 897594128,
- 'node_id': 'IC_kwDODunzps41gDMQ',
- 'user': {'login': 'bhavitvyamalik',
- 'id': 19718818,
- 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
- 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
- 'gravatar_id': '',
- 'url': 'https://api.github.com/users/bhavitvyamalik',
- 'html_url': 'https://github.com/bhavitvyamalik',
- 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
- 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
- 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
- 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
- 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
- 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
- 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
- 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
- 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
- 'type': 'User',
- 'site_admin': False},
- 'created_at': '2021-08-12T12:21:52Z',
- 'updated_at': '2021-08-12T12:31:17Z',
- 'author_association': 'CONTRIBUTOR',
- 'body': "@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?",
- 'performed_via_github_app': None}]
-```
-
-We can see that the comment is stored in the `body` field, so let's write a simple function that returns all the comments associated with an issue by picking out the `body` contents for each element in `response.json()`:
-
-```py
-def get_comments(issue_number):
- url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
- response = requests.get(url, headers=headers)
- return [r["body"] for r in response.json()]
-
-
-# Test our function works as expected
-get_comments(2792)
-```
-
-```python out
-["@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?"]
-```
-
-This looks good, so let's use `Dataset.map()` to add a new `comments` column to each issue in our dataset:
-
-```py
-# Depending on your internet connection, this can take a few minutes...
-issues_with_comments_dataset = issues_dataset.map(
- lambda x: {"comments": get_comments(x["number"])}
-)
-```
-
-The final step is to push our dataset to the Hub. Let's take a look at how we can do that.
-
-## Uploading the dataset to the Hugging Face Hub[[uploading-the-dataset-to-the-hugging-face-hub]]
-
-
-
-Now that we have our augmented dataset, it's time to push it to the Hub so we can share it with the community! Uploading a dataset is very simple: just like models and tokenizers from 🤗 Transformers, we can use a `push_to_hub()` method to push a dataset. To do that we need an authentication token, which can be obtained by first logging into the Hugging Face Hub with the `notebook_login()` function:
-
-```py
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-This will create a widget where you can enter your username and password, and an API token will be saved in *~/.huggingface/token*. If you're running the code in a terminal, you can log in via the CLI instead:
-
-```bash
-huggingface-cli login
-```
-
-Once we've done this, we can upload our dataset by running:
-
-```py
-issues_with_comments_dataset.push_to_hub("github-issues")
-```
-
-From here, anyone can download the dataset by simply providing `load_dataset()` with the repository ID as the `path` argument:
-
-```py
-remote_dataset = load_dataset("lewtun/github-issues", split="train")
-remote_dataset
-```
-
-```python out
-Dataset({
- features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
- num_rows: 2855
-})
-```
-
-Cool, we've pushed our dataset to the Hub and it's available for others to use! There's just one important thing left to do: adding a _dataset card_ that explains how the corpus was created and provides other useful information for the community.
-
-> [!TIP]
-> 💡 You can also upload a dataset to the Hugging Face Hub directly from the terminal by using `huggingface-cli` and a bit of Git magic. See the [🤗 Datasets guide](https://huggingface.co/docs/datasets/share#share-a-dataset-using-the-cli) for details on how to do this.
-
-## Creating a dataset card[[creating-a-dataset-card]]
-
-Well-documented datasets are more likely to be useful to others (including your future self!), as they provide the context to enable users to decide whether the dataset is relevant to their task and to evaluate any potential biases in or risks associated with using the dataset.
-
-On the Hugging Face Hub, this information is stored in each dataset repository's *README.md* file. There are two main steps you should take before creating this file:
-
-1. Use the [`datasets-tagging` application](https://huggingface.co/datasets/tagging/) to create metadata tags in YAML format. These tags are used for a variety of search features on the Hugging Face Hub and ensure your dataset can be easily found by members of the community. Since we have created a custom dataset here, you'll need to clone the `datasets-tagging` repository and run the application locally. Here's what the interface looks like:
-
-
-

-
-
-2. Read the [🤗 Datasets guide](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) on creating informative dataset cards and use it as a template.
-
-You can create the *README.md* file directly on the Hub, and you can find a template dataset card in the `lewtun/github-issues` dataset repository. A screenshot of the filled-out dataset card is shown below.
-
-
-

-
-
-> [!TIP]
-> ✏️ **Try it out!** Use the `dataset-tagging` application and [🤗 Datasets guide](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) to complete the *README.md* file for your GitHub issues dataset.
-
-That's it! We've seen in this section that creating a good dataset can be quite involved, but fortunately uploading it and sharing it with the community is not. In the next section we'll use our new dataset to create a semantic search engine with 🤗 Datasets that can match questions to the most relevant issues and comments.
-
-> [!TIP]
-> ✏️ **Try it out!** Go through the steps we took in this section to create a dataset of GitHub issues for your favorite open source library (pick something other than 🤗 Datasets, of course!). For bonus points, fine-tune a multilabel classifier to predict the tags present in the `labels` field.
-
-
+# Creating your own dataset[[creating-your-own-dataset]]
+
+
+
+Sometimes the dataset that you need to build an NLP application doesn't exist, so you'll need to create it yourself. In this section we'll show you how to create a corpus of [GitHub issues](https://github.com/features/issues/), which are commonly used to track bugs or features in GitHub repositories. This corpus could be used for various purposes, including:
+
+* Exploring how long it takes to close open issues or pull requests
+* Training a _multilabel classifier_ that can tag issues with metadata based on the issue's description (e.g., "bug," "enhancement," or "question")
+* Creating a semantic search engine to find which issues match a user's query
+
+Here we'll focus on creating the corpus, and in the next section we'll tackle the semantic search application. To keep things meta, we'll use the GitHub issues associated with a popular open source project: 🤗 Datasets! Let's take a look at how to get the data and explore the information contained in these issues.
+
+## Getting the data[[getting-the-data]]
+
+You can find all the issues in 🤗 Datasets by navigating to the repository's [Issues tab](https://github.com/huggingface/datasets/issues). As shown in the following screenshot, at the time of writing there were 331 open issues and 668 closed ones.
+
+
+

+
+
+If you click on one of these issues you'll find it contains a title, a description, and a set of labels that characterize the issue. An example is shown in the screenshot below.
+
+
+

+
+
+To download all the repository's issues, we'll use the [GitHub REST API](https://docs.github.com/en/rest) to poll the [`Issues` endpoint](https://docs.github.com/en/rest/reference/issues#list-repository-issues). This endpoint returns a list of JSON objects, with each object containing a large number of fields that include the title and description as well as metadata about the status of the issue and so on.
+
+A convenient way to download the issues is via the `requests` library, which is the standard way for making HTTP requests in Python. You can install the library by running:
+
+```python
+!pip install requests
+```
+
+Once the library is installed, you can make GET requests to the `Issues` endpoint by invoking the `requests.get()` function. For example, you can run the following command to retrieve the first issue on the first page:
+
+```py
+import requests
+
+url = "https://api.github.com/repos/huggingface/datasets/issues?page=1&per_page=1"
+response = requests.get(url)
+```
+
+The `response` object contains a lot of useful information about the request, including the HTTP status code:
+
+```py
+response.status_code
+```
+
+```python out
+200
+```
+
+where a `200` status means the request was successful (you can find a list of possible HTTP status codes [here](https://en.wikipedia.org/wiki/List_of_HTTP_status_codes)). What we are really interested in, though, is the _payload_, which can be accessed in various formats like bytes, strings, or JSON. Since we know our issues are in JSON format, let's inspect the payload as follows:
+
+```py
+response.json()
+```
+
+```python out
+[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
+ 'repository_url': 'https://api.github.com/repos/huggingface/datasets',
+ 'labels_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/labels{/name}',
+ 'comments_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/comments',
+ 'events_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792/events',
+ 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
+ 'id': 968650274,
+ 'node_id': 'MDExOlB1bGxSZXF1ZXN0NzEwNzUyMjc0',
+ 'number': 2792,
+ 'title': 'Update GooAQ',
+ 'user': {'login': 'bhavitvyamalik',
+ 'id': 19718818,
+ 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
+ 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
+ 'gravatar_id': '',
+ 'url': 'https://api.github.com/users/bhavitvyamalik',
+ 'html_url': 'https://github.com/bhavitvyamalik',
+ 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
+ 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
+ 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
+ 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
+ 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
+ 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
+ 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
+ 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
+ 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
+ 'type': 'User',
+ 'site_admin': False},
+ 'labels': [],
+ 'state': 'open',
+ 'locked': False,
+ 'assignee': None,
+ 'assignees': [],
+ 'milestone': None,
+ 'comments': 1,
+ 'created_at': '2021-08-12T11:40:18Z',
+ 'updated_at': '2021-08-12T12:31:17Z',
+ 'closed_at': None,
+ 'author_association': 'CONTRIBUTOR',
+ 'active_lock_reason': None,
+ 'pull_request': {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/2792',
+ 'html_url': 'https://github.com/huggingface/datasets/pull/2792',
+ 'diff_url': 'https://github.com/huggingface/datasets/pull/2792.diff',
+ 'patch_url': 'https://github.com/huggingface/datasets/pull/2792.patch'},
+ 'body': '[GooAQ](https://github.com/allenai/gooaq) dataset was recently updated after splits were added for the same. This PR contains new updated GooAQ with train/val/test splits and updated README as well.',
+ 'performed_via_github_app': None}]
+```
+
+Whoa, that's a lot of information! We can see useful fields like `title`, `body`, and `number` that describe the issue, as well as information about the GitHub user who opened the issue.
+
+> [!TIP]
+> ✏️ **Try it out!** Click on a few of the URLs in the JSON payload above to get a feel for what type of information each GitHub issue is linked to.
+
+As described in the GitHub [documentation](https://docs.github.com/en/rest/overview/resources-in-the-rest-api#rate-limiting), unauthenticated requests are limited to 60 requests per hour. Although you can increase the `per_page` query parameter to reduce the number of requests you make, you will still hit the rate limit on any repository that has more than a few thousand issues. So instead, you should follow GitHub's [instructions](https://docs.github.com/en/github/authenticating-to-github/creating-a-personal-access-token) on creating a _personal access token_ so that you can boost the rate limit to 5,000 requests per hour. Once you have your token, you can include it as part of the request header:
+
+```py
+GITHUB_TOKEN = xxx # Copy your GitHub token here
+headers = {"Authorization": f"token {GITHUB_TOKEN}"}
+```
+
+> [!WARNING]
+> ⚠️ Do not share a notebook with your `GITHUB_TOKEN` pasted in it. We recommend you delete the last cell once you have executed it to avoid leaking this information accidentally. Even better, store the token in a *.env* file and use the [`python-dotenv` library](https://github.com/theskumar/python-dotenv) to load it automatically for you as an environment variable.
+
+Now that we have our access token, let's create a function that can download all the issues from a GitHub repository:
+
+```py
+import time
+import math
+from pathlib import Path
+import pandas as pd
+from tqdm.notebook import tqdm
+
+
+def fetch_issues(
+ owner="huggingface",
+ repo="datasets",
+ num_issues=10_000,
+ rate_limit=5_000,
+ issues_path=Path("."),
+):
+ if not issues_path.is_dir():
+ issues_path.mkdir(exist_ok=True)
+
+ batch = []
+ all_issues = []
+ per_page = 100 # Number of issues to return per page
+ num_pages = math.ceil(num_issues / per_page)
+ base_url = "https://api.github.com/repos"
+
+ for page in tqdm(range(num_pages)):
+ # Query with state=all to get both open and closed issues
+ query = f"issues?page={page}&per_page={per_page}&state=all"
+ issues = requests.get(f"{base_url}/{owner}/{repo}/{query}", headers=headers)
+ batch.extend(issues.json())
+
+ if len(batch) > rate_limit and len(all_issues) < num_issues:
+ all_issues.extend(batch)
+ batch = [] # Flush batch for next time period
+ print(f"Reached GitHub rate limit. Sleeping for one hour ...")
+ time.sleep(60 * 60 + 1)
+
+ all_issues.extend(batch)
+ df = pd.DataFrame.from_records(all_issues)
+ df.to_json(f"{issues_path}/{repo}-issues.jsonl", orient="records", lines=True)
+ print(
+ f"Downloaded all the issues for {repo}! Dataset stored at {issues_path}/{repo}-issues.jsonl"
+ )
+```
+
+Now when we call `fetch_issues()` it will download all the issues in batches to avoid exceeding GitHub's limit on the number of requests per hour; the result will be stored in a _repository_name-issues.jsonl_ file, where each line is a JSON object the represents an issue. Let's use this function to grab all the issues from 🤗 Datasets:
+
+```py
+# Depending on your internet connection, this can take several minutes to run...
+fetch_issues()
+```
+
+Once the issues are downloaded we can load them locally using our newfound skills from [section 2](/course/chapter5/2):
+
+```py
+issues_dataset = load_dataset("json", data_files="datasets-issues.jsonl", split="train")
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'timeline_url', 'performed_via_github_app'],
+ num_rows: 3019
+})
+```
+
+Great, we've created our first dataset from scratch! But why are there several thousand issues when the [Issues tab](https://github.com/huggingface/datasets/issues) of the 🤗 Datasets repository only shows around 1,000 issues in total 🤔? As described in the GitHub [documentation](https://docs.github.com/en/rest/reference/issues#list-issues-assigned-to-the-authenticated-user), that's because we've downloaded all the pull requests as well:
+
+> GitHub's REST API v3 considers every pull request an issue, but not every issue is a pull request. For this reason, "Issues" endpoints may return both issues and pull requests in the response. You can identify pull requests by the `pull_request` key. Be aware that the `id` of a pull request returned from "Issues" endpoints will be an issue id.
+
+Since the contents of issues and pull requests are quite different, let's do some minor preprocessing to enable us to distinguish between them.
+
+## Cleaning up the data[[cleaning-up-the-data]]
+
+The above snippet from GitHub's documentation tells us that the `pull_request` column can be used to differentiate between issues and pull requests. Let's look at a random sample to see what the difference is. As we did in [section 3](/course/chapter5/3), we'll chain `Dataset.shuffle()` and `Dataset.select()` to create a random sample and then zip the `html_url` and `pull_request` columns so we can compare the various URLs:
+
+```py
+sample = issues_dataset.shuffle(seed=666).select(range(3))
+
+# Print out the URL and pull request entries
+for url, pr in zip(sample["html_url"], sample["pull_request"]):
+ print(f">> URL: {url}")
+ print(f">> Pull request: {pr}\n")
+```
+
+```python out
+>> URL: https://github.com/huggingface/datasets/pull/850
+>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/850', 'html_url': 'https://github.com/huggingface/datasets/pull/850', 'diff_url': 'https://github.com/huggingface/datasets/pull/850.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/850.patch'}
+
+>> URL: https://github.com/huggingface/datasets/issues/2773
+>> Pull request: None
+
+>> URL: https://github.com/huggingface/datasets/pull/783
+>> Pull request: {'url': 'https://api.github.com/repos/huggingface/datasets/pulls/783', 'html_url': 'https://github.com/huggingface/datasets/pull/783', 'diff_url': 'https://github.com/huggingface/datasets/pull/783.diff', 'patch_url': 'https://github.com/huggingface/datasets/pull/783.patch'}
+```
+
+Here we can see that each pull request is associated with various URLs, while ordinary issues have a `None` entry. We can use this distinction to create a new `is_pull_request` column that checks whether the `pull_request` field is `None` or not:
+
+```py
+issues_dataset = issues_dataset.map(
+ lambda x: {"is_pull_request": False if x["pull_request"] is None else True}
+)
+```
+
+> [!TIP]
+> ✏️ **Try it out!** Calculate the average time it takes to close issues in 🤗 Datasets. You may find the `Dataset.filter()` function useful to filter out the pull requests and open issues, and you can use the `Dataset.set_format()` function to convert the dataset to a `DataFrame` so you can easily manipulate the `created_at` and `closed_at` timestamps. For bonus points, calculate the average time it takes to close pull requests.
+
+Although we could proceed to further clean up the dataset by dropping or renaming some columns, it is generally a good practice to keep the dataset as "raw" as possible at this stage so that it can be easily used in multiple applications.
+
+Before we push our dataset to the Hugging Face Hub, let's deal with one thing that's missing from it: the comments associated with each issue and pull request. We'll add them next with -- you guessed it -- the GitHub REST API!
+
+## Augmenting the dataset[[augmenting-the-dataset]]
+
+As shown in the following screenshot, the comments associated with an issue or pull request provide a rich source of information, especially if we're interested in building a search engine to answer user queries about the library.
+
+
+

+
+
+The GitHub REST API provides a [`Comments` endpoint](https://docs.github.com/en/rest/reference/issues#list-issue-comments) that returns all the comments associated with an issue number. Let's test the endpoint to see what it returns:
+
+```py
+issue_number = 2792
+url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
+response = requests.get(url, headers=headers)
+response.json()
+```
+
+```python out
+[{'url': 'https://api.github.com/repos/huggingface/datasets/issues/comments/897594128',
+ 'html_url': 'https://github.com/huggingface/datasets/pull/2792#issuecomment-897594128',
+ 'issue_url': 'https://api.github.com/repos/huggingface/datasets/issues/2792',
+ 'id': 897594128,
+ 'node_id': 'IC_kwDODunzps41gDMQ',
+ 'user': {'login': 'bhavitvyamalik',
+ 'id': 19718818,
+ 'node_id': 'MDQ6VXNlcjE5NzE4ODE4',
+ 'avatar_url': 'https://avatars.githubusercontent.com/u/19718818?v=4',
+ 'gravatar_id': '',
+ 'url': 'https://api.github.com/users/bhavitvyamalik',
+ 'html_url': 'https://github.com/bhavitvyamalik',
+ 'followers_url': 'https://api.github.com/users/bhavitvyamalik/followers',
+ 'following_url': 'https://api.github.com/users/bhavitvyamalik/following{/other_user}',
+ 'gists_url': 'https://api.github.com/users/bhavitvyamalik/gists{/gist_id}',
+ 'starred_url': 'https://api.github.com/users/bhavitvyamalik/starred{/owner}{/repo}',
+ 'subscriptions_url': 'https://api.github.com/users/bhavitvyamalik/subscriptions',
+ 'organizations_url': 'https://api.github.com/users/bhavitvyamalik/orgs',
+ 'repos_url': 'https://api.github.com/users/bhavitvyamalik/repos',
+ 'events_url': 'https://api.github.com/users/bhavitvyamalik/events{/privacy}',
+ 'received_events_url': 'https://api.github.com/users/bhavitvyamalik/received_events',
+ 'type': 'User',
+ 'site_admin': False},
+ 'created_at': '2021-08-12T12:21:52Z',
+ 'updated_at': '2021-08-12T12:31:17Z',
+ 'author_association': 'CONTRIBUTOR',
+ 'body': "@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?",
+ 'performed_via_github_app': None}]
+```
+
+We can see that the comment is stored in the `body` field, so let's write a simple function that returns all the comments associated with an issue by picking out the `body` contents for each element in `response.json()`:
+
+```py
+def get_comments(issue_number):
+ url = f"https://api.github.com/repos/huggingface/datasets/issues/{issue_number}/comments"
+ response = requests.get(url, headers=headers)
+ return [r["body"] for r in response.json()]
+
+
+# Test our function works as expected
+get_comments(2792)
+```
+
+```python out
+["@albertvillanova my tests are failing here:\r\n```\r\ndataset_name = 'gooaq'\r\n\r\n def test_load_dataset(self, dataset_name):\r\n configs = self.dataset_tester.load_all_configs(dataset_name, is_local=True)[:1]\r\n> self.dataset_tester.check_load_dataset(dataset_name, configs, is_local=True, use_local_dummy_data=True)\r\n\r\ntests/test_dataset_common.py:234: \r\n_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ \r\ntests/test_dataset_common.py:187: in check_load_dataset\r\n self.parent.assertTrue(len(dataset[split]) > 0)\r\nE AssertionError: False is not true\r\n```\r\nWhen I try loading dataset on local machine it works fine. Any suggestions on how can I avoid this error?"]
+```
+
+This looks good, so let's use `Dataset.map()` to add a new `comments` column to each issue in our dataset:
+
+```py
+# Depending on your internet connection, this can take a few minutes...
+issues_with_comments_dataset = issues_dataset.map(
+ lambda x: {"comments": get_comments(x["number"])}
+)
+```
+
+The final step is to push our dataset to the Hub. Let's take a look at how we can do that.
+
+## Uploading the dataset to the Hugging Face Hub[[uploading-the-dataset-to-the-hugging-face-hub]]
+
+
+
+Now that we have our augmented dataset, it's time to push it to the Hub so we can share it with the community! Uploading a dataset is very simple: just like models and tokenizers from 🤗 Transformers, we can use a `push_to_hub()` method to push a dataset. To do that we need an authentication token, which can be obtained by first logging into the Hugging Face Hub with the `notebook_login()` function:
+
+```py
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+This will create a widget where you can enter your username and password, and an API token will be saved in *~/.huggingface/token*. If you're running the code in a terminal, you can log in via the CLI instead:
+
+```bash
+huggingface-cli login
+```
+
+Once we've done this, we can upload our dataset by running:
+
+```py
+issues_with_comments_dataset.push_to_hub("github-issues")
+```
+
+From here, anyone can download the dataset by simply providing `load_dataset()` with the repository ID as the `path` argument:
+
+```py
+remote_dataset = load_dataset("lewtun/github-issues", split="train")
+remote_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
+ num_rows: 2855
+})
+```
+
+Cool, we've pushed our dataset to the Hub and it's available for others to use! There's just one important thing left to do: adding a _dataset card_ that explains how the corpus was created and provides other useful information for the community.
+
+> [!TIP]
+> 💡 You can also upload a dataset to the Hugging Face Hub directly from the terminal by using `huggingface-cli` and a bit of Git magic. See the [🤗 Datasets guide](https://huggingface.co/docs/datasets/share#share-a-dataset-using-the-cli) for details on how to do this.
+
+## Creating a dataset card[[creating-a-dataset-card]]
+
+Well-documented datasets are more likely to be useful to others (including your future self!), as they provide the context to enable users to decide whether the dataset is relevant to their task and to evaluate any potential biases in or risks associated with using the dataset.
+
+On the Hugging Face Hub, this information is stored in each dataset repository's *README.md* file. There are two main steps you should take before creating this file:
+
+1. Use the [`datasets-tagging` application](https://huggingface.co/datasets/tagging/) to create metadata tags in YAML format. These tags are used for a variety of search features on the Hugging Face Hub and ensure your dataset can be easily found by members of the community. Since we have created a custom dataset here, you'll need to clone the `datasets-tagging` repository and run the application locally. Here's what the interface looks like:
+
+
+

+
+
+2. Read the [🤗 Datasets guide](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) on creating informative dataset cards and use it as a template.
+
+You can create the *README.md* file directly on the Hub, and you can find a template dataset card in the `lewtun/github-issues` dataset repository. A screenshot of the filled-out dataset card is shown below.
+
+
+

+
+
+> [!TIP]
+> ✏️ **Try it out!** Use the `dataset-tagging` application and [🤗 Datasets guide](https://github.com/huggingface/datasets/blob/master/templates/README_guide.md) to complete the *README.md* file for your GitHub issues dataset.
+
+That's it! We've seen in this section that creating a good dataset can be quite involved, but fortunately uploading it and sharing it with the community is not. In the next section we'll use our new dataset to create a semantic search engine with 🤗 Datasets that can match questions to the most relevant issues and comments.
+
+> [!TIP]
+> ✏️ **Try it out!** Go through the steps we took in this section to create a dataset of GitHub issues for your favorite open source library (pick something other than 🤗 Datasets, of course!). For bonus points, fine-tune a multilabel classifier to predict the tags present in the `labels` field.
+
+
diff --git a/chapters/en/chapter5/6.mdx b/chapters/en/chapter5/6.mdx
index e7dfbbf49..d1cc3a590 100644
--- a/chapters/en/chapter5/6.mdx
+++ b/chapters/en/chapter5/6.mdx
@@ -1,512 +1,512 @@
-
-
-# Semantic search with FAISS[[semantic-search-with-faiss]]
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-In [section 5](/course/chapter5/5), we created a dataset of GitHub issues and comments from the 🤗 Datasets repository. In this section we'll use this information to build a search engine that can help us find answers to our most pressing questions about the library!
-
-
-
-## Using embeddings for semantic search[[using-embeddings-for-semantic-search]]
-
-As we saw in [Chapter 1](/course/chapter1), Transformer-based language models represent each token in a span of text as an _embedding vector_. It turns out that one can "pool" the individual embeddings to create a vector representation for whole sentences, paragraphs, or (in some cases) documents. These embeddings can then be used to find similar documents in the corpus by computing the dot-product similarity (or some other similarity metric) between each embedding and returning the documents with the greatest overlap.
-
-In this section we'll use embeddings to develop a semantic search engine. These search engines offer several advantages over conventional approaches that are based on matching keywords in a query with the documents.
-
-
-

-

-
-
-## Loading and preparing the dataset[[loading-and-preparing-the-dataset]]
-
-The first thing we need to do is download our dataset of GitHub issues, so let's use `load_dataset()` function as usual:
-
-```py
-from datasets import load_dataset
-
-issues_dataset = load_dataset("lewtun/github-issues", split="train")
-issues_dataset
-```
-
-```python out
-Dataset({
- features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
- num_rows: 2855
-})
-```
-
-Here we've specified the default `train` split in `load_dataset()`, so it returns a `Dataset` instead of a `DatasetDict`. The first order of business is to filter out the pull requests, as these tend to be rarely used for answering user queries and will introduce noise in our search engine. As should be familiar by now, we can use the `Dataset.filter()` function to exclude these rows in our dataset. While we're at it, let's also filter out rows with no comments, since these provide no answers to user queries:
-
-```py
-issues_dataset = issues_dataset.filter(
- lambda x: (x["is_pull_request"] == False and len(x["comments"]) > 0)
-)
-issues_dataset
-```
-
-```python out
-Dataset({
- features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
- num_rows: 771
-})
-```
-
-We can see that there are a lot of columns in our dataset, most of which we don't need to build our search engine. From a search perspective, the most informative columns are `title`, `body`, and `comments`, while `html_url` provides us with a link back to the source issue. Let's use the `Dataset.remove_columns()` function to drop the rest:
-
-```py
-columns = issues_dataset.column_names
-columns_to_keep = ["title", "body", "html_url", "comments"]
-columns_to_remove = set(columns_to_keep).symmetric_difference(columns)
-issues_dataset = issues_dataset.remove_columns(columns_to_remove)
-issues_dataset
-```
-
-```python out
-Dataset({
- features: ['html_url', 'title', 'comments', 'body'],
- num_rows: 771
-})
-```
-
-To create our embeddings we'll augment each comment with the issue's title and body, since these fields often include useful contextual information. Because our `comments` column is currently a list of comments for each issue, we need to "explode" the column so that each row consists of an `(html_url, title, body, comment)` tuple. In Pandas we can do this with the [`DataFrame.explode()` function](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html), which creates a new row for each element in a list-like column, while replicating all the other column values. To see this in action, let's first switch to the Pandas `DataFrame` format:
-
-```py
-issues_dataset.set_format("pandas")
-df = issues_dataset[:]
-```
-
-If we inspect the first row in this `DataFrame` we can see there are four comments associated with this issue:
-
-```py
-df["comments"][0].tolist()
-```
-
-```python out
-['the bug code locate in :\r\n if data_args.task_name is not None:\r\n # Downloading and loading a dataset from the hub.\r\n datasets = load_dataset("glue", data_args.task_name, cache_dir=model_args.cache_dir)',
- 'Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com\r\n\r\nNormally, it should work if you wait a little and then retry.\r\n\r\nCould you please confirm if the problem persists?',
- 'cannot connect,even by Web browser,please check that there is some problems。',
- 'I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...']
-```
-
-When we explode `df`, we expect to get one row for each of these comments. Let's check if that's the case:
-
-```py
-comments_df = df.explode("comments", ignore_index=True)
-comments_df.head(4)
-```
-
-
-
-
- |
- html_url |
- title |
- comments |
- body |
-
-
-
-
- | 0 |
- https://github.com/huggingface/datasets/issues/2787 |
- ConnectionError: Couldn't reach https://raw.githubusercontent.com |
- the bug code locate in :\r\n if data_args.task_name is not None... |
- Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
-
-
- | 1 |
- https://github.com/huggingface/datasets/issues/2787 |
- ConnectionError: Couldn't reach https://raw.githubusercontent.com |
- Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com... |
- Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
-
-
- | 2 |
- https://github.com/huggingface/datasets/issues/2787 |
- ConnectionError: Couldn't reach https://raw.githubusercontent.com |
- cannot connect,even by Web browser,please check that there is some problems。 |
- Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
-
-
- | 3 |
- https://github.com/huggingface/datasets/issues/2787 |
- ConnectionError: Couldn't reach https://raw.githubusercontent.com |
- I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem... |
- Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
-
-
-
-
-Great, we can see the rows have been replicated, with the `comments` column containing the individual comments! Now that we're finished with Pandas, we can quickly switch back to a `Dataset` by loading the `DataFrame` in memory:
-
-```py
-from datasets import Dataset
-
-comments_dataset = Dataset.from_pandas(comments_df)
-comments_dataset
-```
-
-```python out
-Dataset({
- features: ['html_url', 'title', 'comments', 'body'],
- num_rows: 2842
-})
-```
-
-Okay, this has given us a few thousand comments to work with!
-
-
-> [!TIP]
-> ✏️ **Try it out!** See if you can use `Dataset.map()` to explode the `comments` column of `issues_dataset` _without_ resorting to the use of Pandas. This is a little tricky; you might find the ["Batch mapping"](https://huggingface.co/docs/datasets/about_map_batch#batch-mapping) section of the 🤗 Datasets documentation useful for this task.
-
-Now that we have one comment per row, let's create a new `comments_length` column that contains the number of words per comment:
-
-```py
-comments_dataset = comments_dataset.map(
- lambda x: {"comment_length": len(x["comments"].split())}
-)
-```
-
-We can use this new column to filter out short comments, which typically include things like "cc @lewtun" or "Thanks!" that are not relevant for our search engine. There's no precise number to select for the filter, but around 15 words seems like a good start:
-
-```py
-comments_dataset = comments_dataset.filter(lambda x: x["comment_length"] > 15)
-comments_dataset
-```
-
-```python out
-Dataset({
- features: ['html_url', 'title', 'comments', 'body', 'comment_length'],
- num_rows: 2098
-})
-```
-
-Having cleaned up our dataset a bit, let's concatenate the issue title, description, and comments together in a new `text` column. As usual, we'll write a simple function that we can pass to `Dataset.map()`:
-
-```py
-def concatenate_text(examples):
- return {
- "text": examples["title"]
- + " \n "
- + examples["body"]
- + " \n "
- + examples["comments"]
- }
-
-
-comments_dataset = comments_dataset.map(concatenate_text)
-```
-
-We're finally ready to create some embeddings! Let's take a look.
-
-## Creating text embeddings[[creating-text-embeddings]]
-
-We saw in [Chapter 2](/course/chapter2) that we can obtain token embeddings by using the `AutoModel` class. All we need to do is pick a suitable checkpoint to load the model from. Fortunately, there's a library called `sentence-transformers` that is dedicated to creating embeddings. As described in the library's [documentation](https://www.sbert.net/examples/applications/semantic-search/README.html#symmetric-vs-asymmetric-semantic-search), our use case is an example of _asymmetric semantic search_ because we have a short query whose answer we'd like to find in a longer document, like a an issue comment. The handy [model overview table](https://www.sbert.net/docs/pretrained_models.html#model-overview) in the documentation indicates that the `multi-qa-mpnet-base-dot-v1` checkpoint has the best performance for semantic search, so we'll use that for our application. We'll also load the tokenizer using the same checkpoint:
-
-{#if fw === 'pt'}
-
-```py
-from transformers import AutoTokenizer, AutoModel
-
-model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
-tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
-model = AutoModel.from_pretrained(model_ckpt)
-```
-
-To speed up the embedding process, it helps to place the model and inputs on a GPU device, so let's do that now:
-
-```py
-import torch
-
-device = torch.device("cuda")
-model.to(device)
-```
-
-{:else}
-
-```py
-from transformers import AutoTokenizer, TFAutoModel
-
-model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
-tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
-model = TFAutoModel.from_pretrained(model_ckpt, from_pt=True)
-```
-
-Note that we've set `from_pt=True` as an argument of the `from_pretrained()` method. That's because the `multi-qa-mpnet-base-dot-v1` checkpoint only has PyTorch weights, so setting `from_pt=True` will automatically convert them to the TensorFlow format for us. As you can see, it is very simple to switch between frameworks in 🤗 Transformers!
-
-{/if}
-
-As we mentioned earlier, we'd like to represent each entry in our GitHub issues corpus as a single vector, so we need to "pool" or average our token embeddings in some way. One popular approach is to perform *CLS pooling* on our model's outputs, where we simply collect the last hidden state for the special `[CLS]` token. The following function does the trick for us:
-
-```py
-def cls_pooling(model_output):
- return model_output.last_hidden_state[:, 0]
-```
-
-Next, we'll create a helper function that will tokenize a list of documents, place the tensors on the GPU, feed them to the model, and finally apply CLS pooling to the outputs:
-
-{#if fw === 'pt'}
-
-```py
-def get_embeddings(text_list):
- encoded_input = tokenizer(
- text_list, padding=True, truncation=True, return_tensors="pt"
- )
- encoded_input = {k: v.to(device) for k, v in encoded_input.items()}
- model_output = model(**encoded_input)
- return cls_pooling(model_output)
-```
-
-We can test the function works by feeding it the first text entry in our corpus and inspecting the output shape:
-
-```py
-embedding = get_embeddings(comments_dataset["text"][0])
-embedding.shape
-```
-
-```python out
-torch.Size([1, 768])
-```
-
-Great, we've converted the first entry in our corpus into a 768-dimensional vector! We can use `Dataset.map()` to apply our `get_embeddings()` function to each row in our corpus, so let's create a new `embeddings` column as follows:
-
-```py
-embeddings_dataset = comments_dataset.map(
- lambda x: {"embeddings": get_embeddings(x["text"]).detach().cpu().numpy()[0]}
-)
-```
-
-{:else}
-
-```py
-def get_embeddings(text_list):
- encoded_input = tokenizer(
- text_list, padding=True, truncation=True, return_tensors="tf"
- )
- encoded_input = {k: v for k, v in encoded_input.items()}
- model_output = model(**encoded_input)
- return cls_pooling(model_output)
-```
-
-We can test the function works by feeding it the first text entry in our corpus and inspecting the output shape:
-
-```py
-embedding = get_embeddings(comments_dataset["text"][0])
-embedding.shape
-```
-
-```python out
-TensorShape([1, 768])
-```
-
-Great, we've converted the first entry in our corpus into a 768-dimensional vector! We can use `Dataset.map()` to apply our `get_embeddings()` function to each row in our corpus, so let's create a new `embeddings` column as follows:
-
-```py
-embeddings_dataset = comments_dataset.map(
- lambda x: {"embeddings": get_embeddings(x["text"]).numpy()[0]}
-)
-```
-
-{/if}
-
-Notice that we've converted the embeddings to NumPy arrays -- that's because 🤗 Datasets requires this format when we try to index them with FAISS, which we'll do next.
-
-
-## Using FAISS for efficient similarity search[[using-faiss-for-efficient-similarity-search]]
-
-Now that we have a dataset of embeddings, we need some way to search over them. To do this, we'll use a special data structure in 🤗 Datasets called a _FAISS index_. [FAISS](https://faiss.ai/) (short for Facebook AI Similarity Search) is a library that provides efficient algorithms to quickly search and cluster embedding vectors.
-
-The basic idea behind FAISS is to create a special data structure called an _index_ that allows one to find which embeddings are similar to an input embedding. Creating a FAISS index in 🤗 Datasets is simple -- we use the `Dataset.add_faiss_index()` function and specify which column of our dataset we'd like to index:
-
-```py
-embeddings_dataset.add_faiss_index(column="embeddings")
-```
-
-We can now perform queries on this index by doing a nearest neighbor lookup with the `Dataset.get_nearest_examples()` function. Let's test this out by first embedding a question as follows:
-
-{#if fw === 'pt'}
-
-```py
-question = "How can I load a dataset offline?"
-question_embedding = get_embeddings([question]).cpu().detach().numpy()
-question_embedding.shape
-```
-
-```python out
-torch.Size([1, 768])
-```
-
-{:else}
-
-```py
-question = "How can I load a dataset offline?"
-question_embedding = get_embeddings([question]).numpy()
-question_embedding.shape
-```
-
-```python out
-(1, 768)
-```
-
-{/if}
-
-Just like with the documents, we now have a 768-dimensional vector representing the query, which we can compare against the whole corpus to find the most similar embeddings:
-
-```py
-scores, samples = embeddings_dataset.get_nearest_examples(
- "embeddings", question_embedding, k=5
-)
-```
-
-The `Dataset.get_nearest_examples()` function returns a tuple of scores that rank the overlap between the query and the document, and a corresponding set of samples (here, the 5 best matches). Let's collect these in a `pandas.DataFrame` so we can easily sort them:
-
-```py
-import pandas as pd
-
-samples_df = pd.DataFrame.from_dict(samples)
-samples_df["scores"] = scores
-samples_df.sort_values("scores", ascending=False, inplace=True)
-```
-
-Now we can iterate over the first few rows to see how well our query matched the available comments:
-
-```py
-for _, row in samples_df.iterrows():
- print(f"COMMENT: {row.comments}")
- print(f"SCORE: {row.scores}")
- print(f"TITLE: {row.title}")
- print(f"URL: {row.html_url}")
- print("=" * 50)
- print()
-```
-
-```python out
-"""
-COMMENT: Requiring online connection is a deal breaker in some cases unfortunately so it'd be great if offline mode is added similar to how `transformers` loads models offline fine.
-
-@mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
-SCORE: 25.505046844482422
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-
-COMMENT: The local dataset builders (csv, text , json and pandas) are now part of the `datasets` package since #1726 :)
-You can now use them offline
-\`\`\`python
-datasets = load_dataset("text", data_files=data_files)
-\`\`\`
-
-We'll do a new release soon
-SCORE: 24.555509567260742
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-
-COMMENT: I opened a PR that allows to reload modules that have already been loaded once even if there's no internet.
-
-Let me know if you know other ways that can make the offline mode experience better. I'd be happy to add them :)
-
-I already note the "freeze" modules option, to prevent local modules updates. It would be a cool feature.
-
-----------
-
-> @mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
-
-Indeed `load_dataset` allows to load remote dataset script (squad, glue, etc.) but also you own local ones.
-For example if you have a dataset script at `./my_dataset/my_dataset.py` then you can do
-\`\`\`python
-load_dataset("./my_dataset")
-\`\`\`
-and the dataset script will generate your dataset once and for all.
-
-----------
-
-About I'm looking into having `csv`, `json`, `text`, `pandas` dataset builders already included in the `datasets` package, so that they are available offline by default, as opposed to the other datasets that require the script to be downloaded.
-cf #1724
-SCORE: 24.14896583557129
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-
-COMMENT: > here is my way to load a dataset offline, but it **requires** an online machine
->
-> 1. (online machine)
->
-> ```
->
-> import datasets
->
-> data = datasets.load_dataset(...)
->
-> data.save_to_disk(/YOUR/DATASET/DIR)
->
-> ```
->
-> 2. copy the dir from online to the offline machine
->
-> 3. (offline machine)
->
-> ```
->
-> import datasets
->
-> data = datasets.load_from_disk(/SAVED/DATA/DIR)
->
-> ```
->
->
->
-> HTH.
-
-
-SCORE: 22.893993377685547
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-
-COMMENT: here is my way to load a dataset offline, but it **requires** an online machine
-1. (online machine)
-\`\`\`
-import datasets
-data = datasets.load_dataset(...)
-data.save_to_disk(/YOUR/DATASET/DIR)
-\`\`\`
-2. copy the dir from online to the offline machine
-3. (offline machine)
-\`\`\`
-import datasets
-data = datasets.load_from_disk(/SAVED/DATA/DIR)
-\`\`\`
-
-HTH.
-SCORE: 22.406635284423828
-TITLE: Discussion using datasets in offline mode
-URL: https://github.com/huggingface/datasets/issues/824
-==================================================
-"""
-```
-
-Not bad! Our second hit seems to match the query.
-
-> [!TIP]
+
+
+# Semantic search with FAISS[[semantic-search-with-faiss]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+In [section 5](/course/chapter5/5), we created a dataset of GitHub issues and comments from the 🤗 Datasets repository. In this section we'll use this information to build a search engine that can help us find answers to our most pressing questions about the library!
+
+
+
+## Using embeddings for semantic search[[using-embeddings-for-semantic-search]]
+
+As we saw in [Chapter 1](/course/chapter1), Transformer-based language models represent each token in a span of text as an _embedding vector_. It turns out that one can "pool" the individual embeddings to create a vector representation for whole sentences, paragraphs, or (in some cases) documents. These embeddings can then be used to find similar documents in the corpus by computing the dot-product similarity (or some other similarity metric) between each embedding and returning the documents with the greatest overlap.
+
+In this section we'll use embeddings to develop a semantic search engine. These search engines offer several advantages over conventional approaches that are based on matching keywords in a query with the documents.
+
+
+

+

+
+
+## Loading and preparing the dataset[[loading-and-preparing-the-dataset]]
+
+The first thing we need to do is download our dataset of GitHub issues, so let's use `load_dataset()` function as usual:
+
+```py
+from datasets import load_dataset
+
+issues_dataset = load_dataset("lewtun/github-issues", split="train")
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
+ num_rows: 2855
+})
+```
+
+Here we've specified the default `train` split in `load_dataset()`, so it returns a `Dataset` instead of a `DatasetDict`. The first order of business is to filter out the pull requests, as these tend to be rarely used for answering user queries and will introduce noise in our search engine. As should be familiar by now, we can use the `Dataset.filter()` function to exclude these rows in our dataset. While we're at it, let's also filter out rows with no comments, since these provide no answers to user queries:
+
+```py
+issues_dataset = issues_dataset.filter(
+ lambda x: (x["is_pull_request"] == False and len(x["comments"]) > 0)
+)
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['url', 'repository_url', 'labels_url', 'comments_url', 'events_url', 'html_url', 'id', 'node_id', 'number', 'title', 'user', 'labels', 'state', 'locked', 'assignee', 'assignees', 'milestone', 'comments', 'created_at', 'updated_at', 'closed_at', 'author_association', 'active_lock_reason', 'pull_request', 'body', 'performed_via_github_app', 'is_pull_request'],
+ num_rows: 771
+})
+```
+
+We can see that there are a lot of columns in our dataset, most of which we don't need to build our search engine. From a search perspective, the most informative columns are `title`, `body`, and `comments`, while `html_url` provides us with a link back to the source issue. Let's use the `Dataset.remove_columns()` function to drop the rest:
+
+```py
+columns = issues_dataset.column_names
+columns_to_keep = ["title", "body", "html_url", "comments"]
+columns_to_remove = set(columns_to_keep).symmetric_difference(columns)
+issues_dataset = issues_dataset.remove_columns(columns_to_remove)
+issues_dataset
+```
+
+```python out
+Dataset({
+ features: ['html_url', 'title', 'comments', 'body'],
+ num_rows: 771
+})
+```
+
+To create our embeddings we'll augment each comment with the issue's title and body, since these fields often include useful contextual information. Because our `comments` column is currently a list of comments for each issue, we need to "explode" the column so that each row consists of an `(html_url, title, body, comment)` tuple. In Pandas we can do this with the [`DataFrame.explode()` function](https://pandas.pydata.org/pandas-docs/stable/reference/api/pandas.DataFrame.explode.html), which creates a new row for each element in a list-like column, while replicating all the other column values. To see this in action, let's first switch to the Pandas `DataFrame` format:
+
+```py
+issues_dataset.set_format("pandas")
+df = issues_dataset[:]
+```
+
+If we inspect the first row in this `DataFrame` we can see there are four comments associated with this issue:
+
+```py
+df["comments"][0].tolist()
+```
+
+```python out
+['the bug code locate in :\r\n if data_args.task_name is not None:\r\n # Downloading and loading a dataset from the hub.\r\n datasets = load_dataset("glue", data_args.task_name, cache_dir=model_args.cache_dir)',
+ 'Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com\r\n\r\nNormally, it should work if you wait a little and then retry.\r\n\r\nCould you please confirm if the problem persists?',
+ 'cannot connect,even by Web browser,please check that there is some problems。',
+ 'I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem...']
+```
+
+When we explode `df`, we expect to get one row for each of these comments. Let's check if that's the case:
+
+```py
+comments_df = df.explode("comments", ignore_index=True)
+comments_df.head(4)
+```
+
+
+
+
+ |
+ html_url |
+ title |
+ comments |
+ body |
+
+
+
+
+ | 0 |
+ https://github.com/huggingface/datasets/issues/2787 |
+ ConnectionError: Couldn't reach https://raw.githubusercontent.com |
+ the bug code locate in :\r\n if data_args.task_name is not None... |
+ Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
+
+
+ | 1 |
+ https://github.com/huggingface/datasets/issues/2787 |
+ ConnectionError: Couldn't reach https://raw.githubusercontent.com |
+ Hi @jinec,\r\n\r\nFrom time to time we get this kind of `ConnectionError` coming from the github.com website: https://raw.githubusercontent.com... |
+ Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
+
+
+ | 2 |
+ https://github.com/huggingface/datasets/issues/2787 |
+ ConnectionError: Couldn't reach https://raw.githubusercontent.com |
+ cannot connect,even by Web browser,please check that there is some problems。 |
+ Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
+
+
+ | 3 |
+ https://github.com/huggingface/datasets/issues/2787 |
+ ConnectionError: Couldn't reach https://raw.githubusercontent.com |
+ I can access https://raw.githubusercontent.com/huggingface/datasets/1.7.0/datasets/glue/glue.py without problem... |
+ Hello,\r\nI am trying to run run_glue.py and it gives me this error... |
+
+
+
+
+Great, we can see the rows have been replicated, with the `comments` column containing the individual comments! Now that we're finished with Pandas, we can quickly switch back to a `Dataset` by loading the `DataFrame` in memory:
+
+```py
+from datasets import Dataset
+
+comments_dataset = Dataset.from_pandas(comments_df)
+comments_dataset
+```
+
+```python out
+Dataset({
+ features: ['html_url', 'title', 'comments', 'body'],
+ num_rows: 2842
+})
+```
+
+Okay, this has given us a few thousand comments to work with!
+
+
+> [!TIP]
+> ✏️ **Try it out!** See if you can use `Dataset.map()` to explode the `comments` column of `issues_dataset` _without_ resorting to the use of Pandas. This is a little tricky; you might find the ["Batch mapping"](https://huggingface.co/docs/datasets/about_map_batch#batch-mapping) section of the 🤗 Datasets documentation useful for this task.
+
+Now that we have one comment per row, let's create a new `comments_length` column that contains the number of words per comment:
+
+```py
+comments_dataset = comments_dataset.map(
+ lambda x: {"comment_length": len(x["comments"].split())}
+)
+```
+
+We can use this new column to filter out short comments, which typically include things like "cc @lewtun" or "Thanks!" that are not relevant for our search engine. There's no precise number to select for the filter, but around 15 words seems like a good start:
+
+```py
+comments_dataset = comments_dataset.filter(lambda x: x["comment_length"] > 15)
+comments_dataset
+```
+
+```python out
+Dataset({
+ features: ['html_url', 'title', 'comments', 'body', 'comment_length'],
+ num_rows: 2098
+})
+```
+
+Having cleaned up our dataset a bit, let's concatenate the issue title, description, and comments together in a new `text` column. As usual, we'll write a simple function that we can pass to `Dataset.map()`:
+
+```py
+def concatenate_text(examples):
+ return {
+ "text": examples["title"]
+ + " \n "
+ + examples["body"]
+ + " \n "
+ + examples["comments"]
+ }
+
+
+comments_dataset = comments_dataset.map(concatenate_text)
+```
+
+We're finally ready to create some embeddings! Let's take a look.
+
+## Creating text embeddings[[creating-text-embeddings]]
+
+We saw in [Chapter 2](/course/chapter2) that we can obtain token embeddings by using the `AutoModel` class. All we need to do is pick a suitable checkpoint to load the model from. Fortunately, there's a library called `sentence-transformers` that is dedicated to creating embeddings. As described in the library's [documentation](https://www.sbert.net/examples/applications/semantic-search/README.html#symmetric-vs-asymmetric-semantic-search), our use case is an example of _asymmetric semantic search_ because we have a short query whose answer we'd like to find in a longer document, like a an issue comment. The handy [model overview table](https://www.sbert.net/docs/pretrained_models.html#model-overview) in the documentation indicates that the `multi-qa-mpnet-base-dot-v1` checkpoint has the best performance for semantic search, so we'll use that for our application. We'll also load the tokenizer using the same checkpoint:
+
+{#if fw === 'pt'}
+
+```py
+from transformers import AutoTokenizer, AutoModel
+
+model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
+tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
+model = AutoModel.from_pretrained(model_ckpt)
+```
+
+To speed up the embedding process, it helps to place the model and inputs on a GPU device, so let's do that now:
+
+```py
+import torch
+
+device = torch.device("cuda")
+model.to(device)
+```
+
+{:else}
+
+```py
+from transformers import AutoTokenizer, TFAutoModel
+
+model_ckpt = "sentence-transformers/multi-qa-mpnet-base-dot-v1"
+tokenizer = AutoTokenizer.from_pretrained(model_ckpt)
+model = TFAutoModel.from_pretrained(model_ckpt, from_pt=True)
+```
+
+Note that we've set `from_pt=True` as an argument of the `from_pretrained()` method. That's because the `multi-qa-mpnet-base-dot-v1` checkpoint only has PyTorch weights, so setting `from_pt=True` will automatically convert them to the TensorFlow format for us. As you can see, it is very simple to switch between frameworks in 🤗 Transformers!
+
+{/if}
+
+As we mentioned earlier, we'd like to represent each entry in our GitHub issues corpus as a single vector, so we need to "pool" or average our token embeddings in some way. One popular approach is to perform *CLS pooling* on our model's outputs, where we simply collect the last hidden state for the special `[CLS]` token. The following function does the trick for us:
+
+```py
+def cls_pooling(model_output):
+ return model_output.last_hidden_state[:, 0]
+```
+
+Next, we'll create a helper function that will tokenize a list of documents, place the tensors on the GPU, feed them to the model, and finally apply CLS pooling to the outputs:
+
+{#if fw === 'pt'}
+
+```py
+def get_embeddings(text_list):
+ encoded_input = tokenizer(
+ text_list, padding=True, truncation=True, return_tensors="pt"
+ )
+ encoded_input = {k: v.to(device) for k, v in encoded_input.items()}
+ model_output = model(**encoded_input)
+ return cls_pooling(model_output)
+```
+
+We can test the function works by feeding it the first text entry in our corpus and inspecting the output shape:
+
+```py
+embedding = get_embeddings(comments_dataset["text"][0])
+embedding.shape
+```
+
+```python out
+torch.Size([1, 768])
+```
+
+Great, we've converted the first entry in our corpus into a 768-dimensional vector! We can use `Dataset.map()` to apply our `get_embeddings()` function to each row in our corpus, so let's create a new `embeddings` column as follows:
+
+```py
+embeddings_dataset = comments_dataset.map(
+ lambda x: {"embeddings": get_embeddings(x["text"]).detach().cpu().numpy()[0]}
+)
+```
+
+{:else}
+
+```py
+def get_embeddings(text_list):
+ encoded_input = tokenizer(
+ text_list, padding=True, truncation=True, return_tensors="tf"
+ )
+ encoded_input = {k: v for k, v in encoded_input.items()}
+ model_output = model(**encoded_input)
+ return cls_pooling(model_output)
+```
+
+We can test the function works by feeding it the first text entry in our corpus and inspecting the output shape:
+
+```py
+embedding = get_embeddings(comments_dataset["text"][0])
+embedding.shape
+```
+
+```python out
+TensorShape([1, 768])
+```
+
+Great, we've converted the first entry in our corpus into a 768-dimensional vector! We can use `Dataset.map()` to apply our `get_embeddings()` function to each row in our corpus, so let's create a new `embeddings` column as follows:
+
+```py
+embeddings_dataset = comments_dataset.map(
+ lambda x: {"embeddings": get_embeddings(x["text"]).numpy()[0]}
+)
+```
+
+{/if}
+
+Notice that we've converted the embeddings to NumPy arrays -- that's because 🤗 Datasets requires this format when we try to index them with FAISS, which we'll do next.
+
+
+## Using FAISS for efficient similarity search[[using-faiss-for-efficient-similarity-search]]
+
+Now that we have a dataset of embeddings, we need some way to search over them. To do this, we'll use a special data structure in 🤗 Datasets called a _FAISS index_. [FAISS](https://faiss.ai/) (short for Facebook AI Similarity Search) is a library that provides efficient algorithms to quickly search and cluster embedding vectors.
+
+The basic idea behind FAISS is to create a special data structure called an _index_ that allows one to find which embeddings are similar to an input embedding. Creating a FAISS index in 🤗 Datasets is simple -- we use the `Dataset.add_faiss_index()` function and specify which column of our dataset we'd like to index:
+
+```py
+embeddings_dataset.add_faiss_index(column="embeddings")
+```
+
+We can now perform queries on this index by doing a nearest neighbor lookup with the `Dataset.get_nearest_examples()` function. Let's test this out by first embedding a question as follows:
+
+{#if fw === 'pt'}
+
+```py
+question = "How can I load a dataset offline?"
+question_embedding = get_embeddings([question]).cpu().detach().numpy()
+question_embedding.shape
+```
+
+```python out
+torch.Size([1, 768])
+```
+
+{:else}
+
+```py
+question = "How can I load a dataset offline?"
+question_embedding = get_embeddings([question]).numpy()
+question_embedding.shape
+```
+
+```python out
+(1, 768)
+```
+
+{/if}
+
+Just like with the documents, we now have a 768-dimensional vector representing the query, which we can compare against the whole corpus to find the most similar embeddings:
+
+```py
+scores, samples = embeddings_dataset.get_nearest_examples(
+ "embeddings", question_embedding, k=5
+)
+```
+
+The `Dataset.get_nearest_examples()` function returns a tuple of scores that rank the overlap between the query and the document, and a corresponding set of samples (here, the 5 best matches). Let's collect these in a `pandas.DataFrame` so we can easily sort them:
+
+```py
+import pandas as pd
+
+samples_df = pd.DataFrame.from_dict(samples)
+samples_df["scores"] = scores
+samples_df.sort_values("scores", ascending=False, inplace=True)
+```
+
+Now we can iterate over the first few rows to see how well our query matched the available comments:
+
+```py
+for _, row in samples_df.iterrows():
+ print(f"COMMENT: {row.comments}")
+ print(f"SCORE: {row.scores}")
+ print(f"TITLE: {row.title}")
+ print(f"URL: {row.html_url}")
+ print("=" * 50)
+ print()
+```
+
+```python out
+"""
+COMMENT: Requiring online connection is a deal breaker in some cases unfortunately so it'd be great if offline mode is added similar to how `transformers` loads models offline fine.
+
+@mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
+SCORE: 25.505046844482422
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+COMMENT: The local dataset builders (csv, text , json and pandas) are now part of the `datasets` package since #1726 :)
+You can now use them offline
+\`\`\`python
+datasets = load_dataset("text", data_files=data_files)
+\`\`\`
+
+We'll do a new release soon
+SCORE: 24.555509567260742
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+COMMENT: I opened a PR that allows to reload modules that have already been loaded once even if there's no internet.
+
+Let me know if you know other ways that can make the offline mode experience better. I'd be happy to add them :)
+
+I already note the "freeze" modules option, to prevent local modules updates. It would be a cool feature.
+
+----------
+
+> @mandubian's second bullet point suggests that there's a workaround allowing you to use your offline (custom?) dataset with `datasets`. Could you please elaborate on how that should look like?
+
+Indeed `load_dataset` allows to load remote dataset script (squad, glue, etc.) but also you own local ones.
+For example if you have a dataset script at `./my_dataset/my_dataset.py` then you can do
+\`\`\`python
+load_dataset("./my_dataset")
+\`\`\`
+and the dataset script will generate your dataset once and for all.
+
+----------
+
+About I'm looking into having `csv`, `json`, `text`, `pandas` dataset builders already included in the `datasets` package, so that they are available offline by default, as opposed to the other datasets that require the script to be downloaded.
+cf #1724
+SCORE: 24.14896583557129
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+COMMENT: > here is my way to load a dataset offline, but it **requires** an online machine
+>
+> 1. (online machine)
+>
+> ```
+>
+> import datasets
+>
+> data = datasets.load_dataset(...)
+>
+> data.save_to_disk(/YOUR/DATASET/DIR)
+>
+> ```
+>
+> 2. copy the dir from online to the offline machine
+>
+> 3. (offline machine)
+>
+> ```
+>
+> import datasets
+>
+> data = datasets.load_from_disk(/SAVED/DATA/DIR)
+>
+> ```
+>
+>
+>
+> HTH.
+
+
+SCORE: 22.893993377685547
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+
+COMMENT: here is my way to load a dataset offline, but it **requires** an online machine
+1. (online machine)
+\`\`\`
+import datasets
+data = datasets.load_dataset(...)
+data.save_to_disk(/YOUR/DATASET/DIR)
+\`\`\`
+2. copy the dir from online to the offline machine
+3. (offline machine)
+\`\`\`
+import datasets
+data = datasets.load_from_disk(/SAVED/DATA/DIR)
+\`\`\`
+
+HTH.
+SCORE: 22.406635284423828
+TITLE: Discussion using datasets in offline mode
+URL: https://github.com/huggingface/datasets/issues/824
+==================================================
+"""
+```
+
+Not bad! Our second hit seems to match the query.
+
+> [!TIP]
> ✏️ **Try it out!** Create your own query and see whether you can find an answer in the retrieved documents. You might have to increase the `k` parameter in `Dataset.get_nearest_examples()` to broaden the search.
\ No newline at end of file
diff --git a/chapters/en/chapter5/7.mdx b/chapters/en/chapter5/7.mdx
index 50ccc65be..041819aa7 100644
--- a/chapters/en/chapter5/7.mdx
+++ b/chapters/en/chapter5/7.mdx
@@ -1,16 +1,16 @@
-# 🤗 Datasets, check![[datasets-check]]
-
-
-
-Well, that was quite a tour through the 🤗 Datasets library -- congratulations on making it this far! With the knowledge that you've gained from this chapter, you should be able to:
-
-- Load datasets from anywhere, be it the Hugging Face Hub, your laptop, or a remote server at your company.
-- Wrangle your data using a mix of the `Dataset.map()` and `Dataset.filter()` functions.
-- Quickly switch between data formats like Pandas and NumPy using `Dataset.set_format()`.
-- Create your very own dataset and push it to the Hugging Face Hub.
-- Embed your documents using a Transformer model and build a semantic search engine using FAISS.
-
+# 🤗 Datasets, check![[datasets-check]]
+
+
+
+Well, that was quite a tour through the 🤗 Datasets library -- congratulations on making it this far! With the knowledge that you've gained from this chapter, you should be able to:
+
+- Load datasets from anywhere, be it the Hugging Face Hub, your laptop, or a remote server at your company.
+- Wrangle your data using a mix of the `Dataset.map()` and `Dataset.filter()` functions.
+- Quickly switch between data formats like Pandas and NumPy using `Dataset.set_format()`.
+- Create your very own dataset and push it to the Hugging Face Hub.
+- Embed your documents using a Transformer model and build a semantic search engine using FAISS.
+
In [Chapter 7](/course/chapter7), we'll put all of this to good use as we take a deep dive into the core NLP tasks that Transformer models are great for. Before jumping ahead, though, put your knowledge of 🤗 Datasets to the test with a quick quiz!
\ No newline at end of file
diff --git a/chapters/en/chapter5/8.mdx b/chapters/en/chapter5/8.mdx
index 27addf6b6..44929de43 100644
--- a/chapters/en/chapter5/8.mdx
+++ b/chapters/en/chapter5/8.mdx
@@ -1,231 +1,231 @@
-
-
-# End-of-chapter quiz[[end-of-chapter-quiz]]
-
-
-
-This chapter covered a lot of ground! Don't worry if you didn't grasp all the details; the next chapters will help you understand how things work under the hood.
-
-Before moving on, though, let's test what you learned in this chapter.
-
-### 1. The `load_dataset()` function in 🤗 Datasets allows you to load a dataset from which of the following locations?
-
-data_files argument of load_dataset() to load local datasets.",
- correct: true
- },
- {
- text: "The Hugging Face Hub",
- explain: "Correct! You can load datasets on the Hub by providing the dataset ID, e.g. load_dataset('emotion').",
- correct: true
- },
- {
- text: "A remote server",
- explain: "Correct! You can pass URLs to the data_files argument of load_dataset() to load remote files.",
- correct: true
- },
- ]}
-/>
-
-### 2. Suppose you load one of the GLUE tasks as follows:
-
-```py
-from datasets import load_dataset
-
-dataset = load_dataset("glue", "mrpc", split="train")
-```
-
-Which of the following commands will produce a random sample of 50 elements from `dataset`?
-
-dataset.sample(50)",
- explain: "This is incorrect -- there is no Dataset.sample() method."
- },
- {
- text: "dataset.shuffle().select(range(50))",
- explain: "Correct! As you saw in this chapter, you first shuffle the dataset and then select the samples from it.",
- correct: true
- },
- {
- text: "dataset.select(range(50)).shuffle()",
- explain: "This is incorrect -- although the code will run, it will only shuffle the first 50 elements in the dataset."
- }
- ]}
-/>
-
-### 3. Suppose you have a dataset about household pets called `pets_dataset`, which has a `name` column that denotes the name of each pet. Which of the following approaches would allow you to filter the dataset for all pets whose names start with the letter "L"?
-
-pets_dataset.filter(lambda x : x['name'].startswith('L'))",
- explain: "Correct! Using a Python lambda function for these quick filters is a great idea. Can you think of another solution?",
- correct: true
- },
- {
- text: "pets_dataset.filter(lambda x['name'].startswith('L'))",
- explain: "This is incorrect -- a lambda function takes the general form lambda *arguments* : *expression*, so you need to provide arguments in this case."
- },
- {
- text: "Create a function like def filter_names(x): return x['name'].startswith('L') and run pets_dataset.filter(filter_names).",
- explain: "Correct! Just like with Dataset.map(), you can pass explicit functions to Dataset.filter(). This is useful when you have some complex logic that isn't suitable for a short lambda function. Which of the other solutions would work?",
- correct: true
- }
- ]}
-/>
-
-### 4. What is memory mapping?
-
-
-
-### 5. Which of the following are the main benefits of memory mapping?
-
-
-
-### 6. Why does the following code fail?
-
-```py
-from datasets import load_dataset
-
-dataset = load_dataset("allocine", streaming=True, split="train")
-dataset[0]
-```
-
-IterableDataset.",
- explain: "Correct! An IterableDataset is a generator, not a container, so you should access its elements using next(iter(dataset)).",
- correct: true
- },
- {
- text: "The allocine dataset doesn't have a train split.",
- explain: "This is incorrect -- check out the [allocine dataset card](https://huggingface.co/datasets/allocine) on the Hub to see which splits it contains."
- }
- ]}
-/>
-
-### 7. Which of the following are the main benefits of creating a dataset card?
-
-
-
-
-### 8. What is semantic search?
-
-
-
-### 9. For asymmetric semantic search, you usually have:
-
-
-
-### 10. Can I use 🤗 Datasets to load data for use in other domains, like speech processing?
-
-MNIST dataset on the Hub for a computer vision example."
- },
- {
- text: "Yes",
- explain: "Correct! Check out the exciting developments with speech and vision in the 🤗 Transformers library to see how 🤗 Datasets is used in these domains.",
- correct : true
- },
- ]}
-/>
+
+
+# End-of-chapter quiz[[end-of-chapter-quiz]]
+
+
+
+This chapter covered a lot of ground! Don't worry if you didn't grasp all the details; the next chapters will help you understand how things work under the hood.
+
+Before moving on, though, let's test what you learned in this chapter.
+
+### 1. The `load_dataset()` function in 🤗 Datasets allows you to load a dataset from which of the following locations?
+
+data_files argument of load_dataset() to load local datasets.",
+ correct: true
+ },
+ {
+ text: "The Hugging Face Hub",
+ explain: "Correct! You can load datasets on the Hub by providing the dataset ID, e.g. load_dataset('emotion').",
+ correct: true
+ },
+ {
+ text: "A remote server",
+ explain: "Correct! You can pass URLs to the data_files argument of load_dataset() to load remote files.",
+ correct: true
+ },
+ ]}
+/>
+
+### 2. Suppose you load one of the GLUE tasks as follows:
+
+```py
+from datasets import load_dataset
+
+dataset = load_dataset("glue", "mrpc", split="train")
+```
+
+Which of the following commands will produce a random sample of 50 elements from `dataset`?
+
+dataset.sample(50)",
+ explain: "This is incorrect -- there is no Dataset.sample() method."
+ },
+ {
+ text: "dataset.shuffle().select(range(50))",
+ explain: "Correct! As you saw in this chapter, you first shuffle the dataset and then select the samples from it.",
+ correct: true
+ },
+ {
+ text: "dataset.select(range(50)).shuffle()",
+ explain: "This is incorrect -- although the code will run, it will only shuffle the first 50 elements in the dataset."
+ }
+ ]}
+/>
+
+### 3. Suppose you have a dataset about household pets called `pets_dataset`, which has a `name` column that denotes the name of each pet. Which of the following approaches would allow you to filter the dataset for all pets whose names start with the letter "L"?
+
+pets_dataset.filter(lambda x : x['name'].startswith('L'))",
+ explain: "Correct! Using a Python lambda function for these quick filters is a great idea. Can you think of another solution?",
+ correct: true
+ },
+ {
+ text: "pets_dataset.filter(lambda x['name'].startswith('L'))",
+ explain: "This is incorrect -- a lambda function takes the general form lambda *arguments* : *expression*, so you need to provide arguments in this case."
+ },
+ {
+ text: "Create a function like def filter_names(x): return x['name'].startswith('L') and run pets_dataset.filter(filter_names).",
+ explain: "Correct! Just like with Dataset.map(), you can pass explicit functions to Dataset.filter(). This is useful when you have some complex logic that isn't suitable for a short lambda function. Which of the other solutions would work?",
+ correct: true
+ }
+ ]}
+/>
+
+### 4. What is memory mapping?
+
+
+
+### 5. Which of the following are the main benefits of memory mapping?
+
+
+
+### 6. Why does the following code fail?
+
+```py
+from datasets import load_dataset
+
+dataset = load_dataset("allocine", streaming=True, split="train")
+dataset[0]
+```
+
+IterableDataset.",
+ explain: "Correct! An IterableDataset is a generator, not a container, so you should access its elements using next(iter(dataset)).",
+ correct: true
+ },
+ {
+ text: "The allocine dataset doesn't have a train split.",
+ explain: "This is incorrect -- check out the [allocine dataset card](https://huggingface.co/datasets/allocine) on the Hub to see which splits it contains."
+ }
+ ]}
+/>
+
+### 7. Which of the following are the main benefits of creating a dataset card?
+
+
+
+
+### 8. What is semantic search?
+
+
+
+### 9. For asymmetric semantic search, you usually have:
+
+
+
+### 10. Can I use 🤗 Datasets to load data for use in other domains, like speech processing?
+
+MNIST dataset on the Hub for a computer vision example."
+ },
+ {
+ text: "Yes",
+ explain: "Correct! Check out the exciting developments with speech and vision in the 🤗 Transformers library to see how 🤗 Datasets is used in these domains.",
+ correct : true
+ },
+ ]}
+/>
diff --git a/chapters/en/chapter6/1.mdx b/chapters/en/chapter6/1.mdx
index ce50bfd4e..1ac5c33eb 100644
--- a/chapters/en/chapter6/1.mdx
+++ b/chapters/en/chapter6/1.mdx
@@ -1,19 +1,19 @@
-# Introduction[[introduction]]
-
-
-
-In [Chapter 3](/course/chapter3), we looked at how to fine-tune a model on a given task. When we do that, we use the same tokenizer that the model was pretrained with -- but what do we do when we want to train a model from scratch? In these cases, using a tokenizer that was pretrained on a corpus from another domain or language is typically suboptimal. For example, a tokenizer that's trained on an English corpus will perform poorly on a corpus of Japanese texts because the use of spaces and punctuation is very different in the two languages.
-
-In this chapter, you will learn how to train a brand new tokenizer on a corpus of texts, so it can then be used to pretrain a language model. This will all be done with the help of the [🤗 Tokenizers](https://github.com/huggingface/tokenizers) library, which provides the "fast" tokenizers in the [🤗 Transformers](https://github.com/huggingface/transformers) library. We'll take a close look at the features that this library provides, and explore how the fast tokenizers differ from the "slow" versions.
-
-Topics we will cover include:
-
-* How to train a new tokenizer similar to the one used by a given checkpoint on a new corpus of texts
-* The special features of fast tokenizers
-* The differences between the three main subword tokenization algorithms used in NLP today
-* How to build a tokenizer from scratch with the 🤗 Tokenizers library and train it on some data
-
+# Introduction[[introduction]]
+
+
+
+In [Chapter 3](/course/chapter3), we looked at how to fine-tune a model on a given task. When we do that, we use the same tokenizer that the model was pretrained with -- but what do we do when we want to train a model from scratch? In these cases, using a tokenizer that was pretrained on a corpus from another domain or language is typically suboptimal. For example, a tokenizer that's trained on an English corpus will perform poorly on a corpus of Japanese texts because the use of spaces and punctuation is very different in the two languages.
+
+In this chapter, you will learn how to train a brand new tokenizer on a corpus of texts, so it can then be used to pretrain a language model. This will all be done with the help of the [🤗 Tokenizers](https://github.com/huggingface/tokenizers) library, which provides the "fast" tokenizers in the [🤗 Transformers](https://github.com/huggingface/transformers) library. We'll take a close look at the features that this library provides, and explore how the fast tokenizers differ from the "slow" versions.
+
+Topics we will cover include:
+
+* How to train a new tokenizer similar to the one used by a given checkpoint on a new corpus of texts
+* The special features of fast tokenizers
+* The differences between the three main subword tokenization algorithms used in NLP today
+* How to build a tokenizer from scratch with the 🤗 Tokenizers library and train it on some data
+
The techniques introduced in this chapter will prepare you for the section in [Chapter 7](/course/chapter7/6) where we look at creating a language model for Python source code. Let's start by looking at what it means to "train" a tokenizer in the first place.
\ No newline at end of file
diff --git a/chapters/en/chapter6/10.mdx b/chapters/en/chapter6/10.mdx
index e1d55634d..a2ce0f9fe 100644
--- a/chapters/en/chapter6/10.mdx
+++ b/chapters/en/chapter6/10.mdx
@@ -1,283 +1,283 @@
-
-
-# End-of-chapter quiz[[end-of-chapter-quiz]]
-
-
-
-Let's test what you learned in this chapter!
-
-### 1. When should you train a new tokenizer?
-
-
-
-### 2. What is the advantage of using a generator of lists of texts compared to a list of lists of texts when using `train_new_from_iterator()`?
-
-train_new_from_iterator() accepts.",
- explain: "A list of lists of texts is a particular kind of generator of lists of texts, so the method will accept this too. Try again!"
- },
- {
- text: "You will avoid loading the whole dataset into memory at once.",
- explain: "Right! Each batch of texts will be released from memory when you iterate, and the gain will be especially visible if you use 🤗 Datasets to store your texts.",
- correct: true
- },
- {
- text: "This will allow the 🤗 Tokenizers library to use multiprocessing.",
- explain: "No, it will use multiprocessing either way."
- },
- {
- text: "The tokenizer you train will generate better texts.",
- explain: "The tokenizer does not generate text -- are you confusing it with a language model?"
- }
- ]}
-/>
-
-### 3. What are the advantages of using a "fast" tokenizer?
-
-
-
-### 4. How does the `token-classification` pipeline handle entities that span over several tokens?
-
-
-
-### 5. How does the `question-answering` pipeline handle long contexts?
-
-
-
-### 6. What is normalization?
-
-
-
-### 7. What is pre-tokenization for a subword tokenizer?
-
-
-
-### 8. Select the sentences that apply to the BPE model of tokenization.
-
-
-
-### 9. Select the sentences that apply to the WordPiece model of tokenization.
-
-
-
-### 10. Select the sentences that apply to the Unigram model of tokenization.
-
-
+
+
+# End-of-chapter quiz[[end-of-chapter-quiz]]
+
+
+
+Let's test what you learned in this chapter!
+
+### 1. When should you train a new tokenizer?
+
+
+
+### 2. What is the advantage of using a generator of lists of texts compared to a list of lists of texts when using `train_new_from_iterator()`?
+
+train_new_from_iterator() accepts.",
+ explain: "A list of lists of texts is a particular kind of generator of lists of texts, so the method will accept this too. Try again!"
+ },
+ {
+ text: "You will avoid loading the whole dataset into memory at once.",
+ explain: "Right! Each batch of texts will be released from memory when you iterate, and the gain will be especially visible if you use 🤗 Datasets to store your texts.",
+ correct: true
+ },
+ {
+ text: "This will allow the 🤗 Tokenizers library to use multiprocessing.",
+ explain: "No, it will use multiprocessing either way."
+ },
+ {
+ text: "The tokenizer you train will generate better texts.",
+ explain: "The tokenizer does not generate text -- are you confusing it with a language model?"
+ }
+ ]}
+/>
+
+### 3. What are the advantages of using a "fast" tokenizer?
+
+
+
+### 4. How does the `token-classification` pipeline handle entities that span over several tokens?
+
+
+
+### 5. How does the `question-answering` pipeline handle long contexts?
+
+
+
+### 6. What is normalization?
+
+
+
+### 7. What is pre-tokenization for a subword tokenizer?
+
+
+
+### 8. Select the sentences that apply to the BPE model of tokenization.
+
+
+
+### 9. Select the sentences that apply to the WordPiece model of tokenization.
+
+
+
+### 10. Select the sentences that apply to the Unigram model of tokenization.
+
+
diff --git a/chapters/en/chapter6/2.mdx b/chapters/en/chapter6/2.mdx
index cd50a0c85..3a5bff061 100644
--- a/chapters/en/chapter6/2.mdx
+++ b/chapters/en/chapter6/2.mdx
@@ -1,254 +1,254 @@
-# Training a new tokenizer from an old one[[training-a-new-tokenizer-from-an-old-one]]
-
-
-
-If a language model is not available in the language you are interested in, or if your corpus is very different from the one your language model was trained on, you will most likely want to retrain the model from scratch using a tokenizer adapted to your data. That will require training a new tokenizer on your dataset. But what exactly does that mean? When we first looked at tokenizers in [Chapter 2](/course/chapter2), we saw that most Transformer models use a _subword tokenization algorithm_. To identify which subwords are of interest and occur most frequently in the corpus at hand, the tokenizer needs to take a hard look at all the texts in the corpus -- a process we call *training*. The exact rules that govern this training depend on the type of tokenizer used, and we'll go over the three main algorithms later in this chapter.
-
-
-
-> [!WARNING]
-> ⚠️ Training a tokenizer is not the same as training a model! Model training uses stochastic gradient descent to make the loss a little bit smaller for each batch. It's randomized by nature (meaning you have to set some seeds to get the same results when doing the same training twice). Training a tokenizer is a statistical process that tries to identify which subwords are the best to pick for a given corpus, and the exact rules used to pick them depend on the tokenization algorithm. It's deterministic, meaning you always get the same results when training with the same algorithm on the same corpus.
-
-## Assembling a corpus[[assembling-a-corpus]]
-
-There's a very simple API in 🤗 Transformers that you can use to train a new tokenizer with the same characteristics as an existing one: `AutoTokenizer.train_new_from_iterator()`. To see this in action, let’s say we want to train GPT-2 from scratch, but in a language other than English. Our first task will be to gather lots of data in that language in a training corpus. To provide examples everyone will be able to understand, we won't use a language like Russian or Chinese here, but rather a specialized English language: Python code.
-
-The [🤗 Datasets](https://github.com/huggingface/datasets) library can help us assemble a corpus of Python source code. We'll use the usual `load_dataset()` function to download and cache the [CodeSearchNet](https://huggingface.co/datasets/code_search_net) dataset. This dataset was created for the [CodeSearchNet challenge](https://wandb.ai/github/CodeSearchNet/benchmark) and contains millions of functions from open source libraries on GitHub in several programming languages. Here, we will load the Python part of this dataset:
-
-```py
-from datasets import load_dataset
-
-# This can take a few minutes to load, so grab a coffee or tea while you wait!
-raw_datasets = load_dataset("code_search_net", "python")
-```
-
-We can have a look at the training split to see which columns we have access to:
-
-```py
-raw_datasets["train"]
-```
-
-```python out
-Dataset({
- features: ['repository_name', 'func_path_in_repository', 'func_name', 'whole_func_string', 'language',
- 'func_code_string', 'func_code_tokens', 'func_documentation_string', 'func_documentation_tokens', 'split_name',
- 'func_code_url'
- ],
- num_rows: 412178
-})
-```
-
-We can see the dataset separates docstrings from code and suggests a tokenization of both. Here. we'll just use the `whole_func_string` column to train our tokenizer. We can look at an example of one these functions by indexing into the `train` split:
-
-```py
-print(raw_datasets["train"][123456]["whole_func_string"])
-```
-
-which should print the following:
-
-```out
-def handle_simple_responses(
- self, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK):
- """Accepts normal responses from the device.
-
- Args:
- timeout_ms: Timeout in milliseconds to wait for each response.
- info_cb: Optional callback for text sent from the bootloader.
-
- Returns:
- OKAY packet's message.
- """
- return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)
-```
-
-The first thing we need to do is transform the dataset into an _iterator_ of lists of texts -- for instance, a list of list of texts. Using lists of texts will enable our tokenizer to go faster (training on batches of texts instead of processing individual texts one by one), and it should be an iterator if we want to avoid having everything in memory at once. If your corpus is huge, you will want to take advantage of the fact that 🤗 Datasets does not load everything into RAM but stores the elements of the dataset on disk.
-
-Doing the following would create a list of lists of 1,000 texts each, but would load everything in memory:
-
-```py
-# Don't uncomment the following line unless your dataset is small!
-# training_corpus = [raw_datasets["train"][i: i + 1000]["whole_func_string"] for i in range(0, len(raw_datasets["train"]), 1000)]
-```
-
-Using a Python generator, we can avoid Python loading anything into memory until it's actually necessary. To create such a generator, you just to need to replace the brackets with parentheses:
-
-```py
-training_corpus = (
- raw_datasets["train"][i : i + 1000]["whole_func_string"]
- for i in range(0, len(raw_datasets["train"]), 1000)
-)
-```
-
-This line of code doesn't fetch any elements of the dataset; it just creates an object you can use in a Python `for` loop. The texts will only be loaded when you need them (that is, when you're at the step of the `for` loop that requires them), and only 1,000 texts at a time will be loaded. This way you won't exhaust all your memory even if you are processing a huge dataset.
-
-The problem with a generator object is that it can only be used once. So, instead of this giving us the list of the first 10 digits twice:
-
-```py
-gen = (i for i in range(10))
-print(list(gen))
-print(list(gen))
-```
-
-we get them once and then an empty list:
-
-```python out
-[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
-[]
-```
-
-That's why we define a function that returns a generator instead:
-
-```py
-def get_training_corpus():
- return (
- raw_datasets["train"][i : i + 1000]["whole_func_string"]
- for i in range(0, len(raw_datasets["train"]), 1000)
- )
-
-
-training_corpus = get_training_corpus()
-```
-
-You can also define your generator inside a `for` loop by using the `yield` statement:
-
-```py
-def get_training_corpus():
- dataset = raw_datasets["train"]
- for start_idx in range(0, len(dataset), 1000):
- samples = dataset[start_idx : start_idx + 1000]
- yield samples["whole_func_string"]
-```
-
-which will produce the exact same generator as before, but allows you to use more complex logic than you can in a list comprehension.
-
-## Training a new tokenizer[[training-a-new-tokenizer]]
-
-Now that we have our corpus in the form of an iterator of batches of texts, we are ready to train a new tokenizer. To do this, we first need to load the tokenizer we want to pair with our model (here, GPT-2):
-
-```py
-from transformers import AutoTokenizer
-
-old_tokenizer = AutoTokenizer.from_pretrained("gpt2")
-```
-
-Even though we are going to train a new tokenizer, it's a good idea to do this to avoid starting entirely from scratch. This way, we won't have to specify anything about the tokenization algorithm or the special tokens we want to use; our new tokenizer will be exactly the same as GPT-2, and the only thing that will change is the vocabulary, which will be determined by the training on our corpus.
-
-First let's have a look at how this tokenizer would treat an example function:
-
-```py
-example = '''def add_numbers(a, b):
- """Add the two numbers `a` and `b`."""
- return a + b'''
-
-tokens = old_tokenizer.tokenize(example)
-tokens
-```
-
-```python out
-['def', 'Ġadd', '_', 'n', 'umbers', '(', 'a', ',', 'Ġb', '):', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo',
- 'Ġnumbers', 'Ġ`', 'a', '`', 'Ġand', 'Ġ`', 'b', '`', '."', '""', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
-```
-
-This tokenizer has a few special symbols, like `Ġ` and `Ċ`, which denote spaces and newlines, respectively. As we can see, this is not too efficient: the tokenizer returns individual tokens for each space, when it could group together indentation levels (since having sets of four or eight spaces is going to be very common in code). It also split the function name a bit weirdly, not being used to seeing words with the `_` character.
-
-Let's train a new tokenizer and see if it solves those issues. For this, we'll use the method `train_new_from_iterator()`:
-
-```py
-tokenizer = old_tokenizer.train_new_from_iterator(training_corpus, 52000)
-```
-
-This command might take a bit of time if your corpus is very large, but for this dataset of 1.6 GB of texts it's blazing fast (1 minute 16 seconds on an AMD Ryzen 9 3900X CPU with 12 cores).
-
-Note that `AutoTokenizer.train_new_from_iterator()` only works if the tokenizer you are using is a "fast" tokenizer. As you'll see in the next section, the 🤗 Transformers library contains two types of tokenizers: some are written purely in Python and others (the fast ones) are backed by the 🤗 Tokenizers library, which is written in the [Rust](https://www.rust-lang.org) programming language. Python is the language most often used for data science and deep learning applications, but when anything needs to be parallelized to be fast, it has to be written in another language. For instance, the matrix multiplications that are at the core of the model computation are written in CUDA, an optimized C library for GPUs.
-
-Training a brand new tokenizer in pure Python would be excruciatingly slow, which is why we developed the 🤗 Tokenizers library. Note that just as you didn't have to learn the CUDA language to be able to execute your model on a batch of inputs on a GPU, you won't need to learn Rust to use a fast tokenizer. The 🤗 Tokenizers library provides Python bindings for many methods that internally call some piece of code in Rust; for example, to parallelize the training of your new tokenizer or, as we saw in [Chapter 3](/course/chapter3), the tokenization of a batch of inputs.
-
-Most of the Transformer models have a fast tokenizer available (there are some exceptions that you can check [here](https://huggingface.co/transformers/#supported-frameworks)), and the `AutoTokenizer` API always selects the fast tokenizer for you if it's available. In the next section we'll take a look at some of the other special features fast tokenizers have, which will be really useful for tasks like token classification and question answering. Before diving into that, however, let's try our brand new tokenizer on the previous example:
-
-```py
-tokens = tokenizer.tokenize(example)
-tokens
-```
-
-```python out
-['def', 'Ġadd', '_', 'numbers', '(', 'a', ',', 'Ġb', '):', 'ĊĠĠĠ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo', 'Ġnumbers', 'Ġ`',
- 'a', '`', 'Ġand', 'Ġ`', 'b', '`."""', 'ĊĠĠĠ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
-```
-
-Here we again see the special symbols `Ġ` and `Ċ` that denote spaces and newlines, but we can also see that our tokenizer learned some tokens that are highly specific to a corpus of Python functions: for example, there is a `ĊĠĠĠ` token that represents an indentation, and a `Ġ"""` token that represents the three quotes that start a docstring. The tokenizer also correctly split the function name on `_`. This is quite a compact representation; comparatively, using the plain English tokenizer on the same example will give us a longer sentence:
-
-```py
-print(len(tokens))
-print(len(old_tokenizer.tokenize(example)))
-```
-
-```python out
-27
-36
-```
-
-Let's look at another example:
-
-```python
-example = """class LinearLayer():
- def __init__(self, input_size, output_size):
- self.weight = torch.randn(input_size, output_size)
- self.bias = torch.zeros(output_size)
-
- def __call__(self, x):
- return x @ self.weights + self.bias
- """
-tokenizer.tokenize(example)
-```
-
-```python out
-['class', 'ĠLinear', 'Layer', '():', 'ĊĠĠĠ', 'Ġdef', 'Ġ__', 'init', '__(', 'self', ',', 'Ġinput', '_', 'size', ',',
- 'Ġoutput', '_', 'size', '):', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'weight', 'Ġ=', 'Ġtorch', '.', 'randn', '(', 'input', '_',
- 'size', ',', 'Ġoutput', '_', 'size', ')', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'bias', 'Ġ=', 'Ġtorch', '.', 'zeros', '(',
- 'output', '_', 'size', ')', 'ĊĊĠĠĠ', 'Ġdef', 'Ġ__', 'call', '__(', 'self', ',', 'Ġx', '):', 'ĊĠĠĠĠĠĠĠ',
- 'Ġreturn', 'Ġx', 'Ġ@', 'Ġself', '.', 'weights', 'Ġ+', 'Ġself', '.', 'bias', 'ĊĠĠĠĠ']
-```
-
-In addition to the token corresponding to an indentation, here we can also see a token for a double indentation: `ĊĠĠĠĠĠĠĠ`. The special Python words like `class`, `init`, `call`, `self`, and `return` are each tokenized as one token, and we can see that as well as splitting on `_` and `.` the tokenizer correctly splits even camel-cased names: `LinearLayer` is tokenized as `["ĠLinear", "Layer"]`.
-
-## Saving the tokenizer[[saving-the-tokenizer]]
-
-To make sure we can use it later, we need to save our new tokenizer. Like for models, this is done with the `save_pretrained()` method:
-
-```py
-tokenizer.save_pretrained("code-search-net-tokenizer")
-```
-
-This will create a new folder named *code-search-net-tokenizer*, which will contain all the files the tokenizer needs to be reloaded. If you want to share this tokenizer with your colleagues and friends, you can upload it to the Hub by logging into your account. If you're working in a notebook, there's a convenience function to help you with this:
-
-```python
-from huggingface_hub import notebook_login
-
-notebook_login()
-```
-
-This will display a widget where you can enter your Hugging Face login credentials. If you aren't working in a notebook, just type the following line in your terminal:
-
-```bash
-huggingface-cli login
-```
-
-Once you've logged in, you can push your tokenizer by executing the following command:
-
-```py
-tokenizer.push_to_hub("code-search-net-tokenizer")
-```
-
-This will create a new repository in your namespace with the name `code-search-net-tokenizer`, containing the tokenizer file. You can then load the tokenizer from anywhere with the `from_pretrained()` method:
-
-```py
-# Replace "huggingface-course" below with your actual namespace to use your own tokenizer
-tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")
-```
-
-You're now all set for training a language model from scratch and fine-tuning it on your task at hand! We'll get to that in [Chapter 7](/course/chapter7), but first, in the rest of this chapter we'll take a closer look at fast tokenizers and explore in detail what actually happens when we call the method `train_new_from_iterator()`.
+# Training a new tokenizer from an old one[[training-a-new-tokenizer-from-an-old-one]]
+
+
+
+If a language model is not available in the language you are interested in, or if your corpus is very different from the one your language model was trained on, you will most likely want to retrain the model from scratch using a tokenizer adapted to your data. That will require training a new tokenizer on your dataset. But what exactly does that mean? When we first looked at tokenizers in [Chapter 2](/course/chapter2), we saw that most Transformer models use a _subword tokenization algorithm_. To identify which subwords are of interest and occur most frequently in the corpus at hand, the tokenizer needs to take a hard look at all the texts in the corpus -- a process we call *training*. The exact rules that govern this training depend on the type of tokenizer used, and we'll go over the three main algorithms later in this chapter.
+
+
+
+> [!WARNING]
+> ⚠️ Training a tokenizer is not the same as training a model! Model training uses stochastic gradient descent to make the loss a little bit smaller for each batch. It's randomized by nature (meaning you have to set some seeds to get the same results when doing the same training twice). Training a tokenizer is a statistical process that tries to identify which subwords are the best to pick for a given corpus, and the exact rules used to pick them depend on the tokenization algorithm. It's deterministic, meaning you always get the same results when training with the same algorithm on the same corpus.
+
+## Assembling a corpus[[assembling-a-corpus]]
+
+There's a very simple API in 🤗 Transformers that you can use to train a new tokenizer with the same characteristics as an existing one: `AutoTokenizer.train_new_from_iterator()`. To see this in action, let’s say we want to train GPT-2 from scratch, but in a language other than English. Our first task will be to gather lots of data in that language in a training corpus. To provide examples everyone will be able to understand, we won't use a language like Russian or Chinese here, but rather a specialized English language: Python code.
+
+The [🤗 Datasets](https://github.com/huggingface/datasets) library can help us assemble a corpus of Python source code. We'll use the usual `load_dataset()` function to download and cache the [CodeSearchNet](https://huggingface.co/datasets/code_search_net) dataset. This dataset was created for the [CodeSearchNet challenge](https://wandb.ai/github/CodeSearchNet/benchmark) and contains millions of functions from open source libraries on GitHub in several programming languages. Here, we will load the Python part of this dataset:
+
+```py
+from datasets import load_dataset
+
+# This can take a few minutes to load, so grab a coffee or tea while you wait!
+raw_datasets = load_dataset("code_search_net", "python")
+```
+
+We can have a look at the training split to see which columns we have access to:
+
+```py
+raw_datasets["train"]
+```
+
+```python out
+Dataset({
+ features: ['repository_name', 'func_path_in_repository', 'func_name', 'whole_func_string', 'language',
+ 'func_code_string', 'func_code_tokens', 'func_documentation_string', 'func_documentation_tokens', 'split_name',
+ 'func_code_url'
+ ],
+ num_rows: 412178
+})
+```
+
+We can see the dataset separates docstrings from code and suggests a tokenization of both. Here. we'll just use the `whole_func_string` column to train our tokenizer. We can look at an example of one these functions by indexing into the `train` split:
+
+```py
+print(raw_datasets["train"][123456]["whole_func_string"])
+```
+
+which should print the following:
+
+```out
+def handle_simple_responses(
+ self, timeout_ms=None, info_cb=DEFAULT_MESSAGE_CALLBACK):
+ """Accepts normal responses from the device.
+
+ Args:
+ timeout_ms: Timeout in milliseconds to wait for each response.
+ info_cb: Optional callback for text sent from the bootloader.
+
+ Returns:
+ OKAY packet's message.
+ """
+ return self._accept_responses('OKAY', info_cb, timeout_ms=timeout_ms)
+```
+
+The first thing we need to do is transform the dataset into an _iterator_ of lists of texts -- for instance, a list of list of texts. Using lists of texts will enable our tokenizer to go faster (training on batches of texts instead of processing individual texts one by one), and it should be an iterator if we want to avoid having everything in memory at once. If your corpus is huge, you will want to take advantage of the fact that 🤗 Datasets does not load everything into RAM but stores the elements of the dataset on disk.
+
+Doing the following would create a list of lists of 1,000 texts each, but would load everything in memory:
+
+```py
+# Don't uncomment the following line unless your dataset is small!
+# training_corpus = [raw_datasets["train"][i: i + 1000]["whole_func_string"] for i in range(0, len(raw_datasets["train"]), 1000)]
+```
+
+Using a Python generator, we can avoid Python loading anything into memory until it's actually necessary. To create such a generator, you just to need to replace the brackets with parentheses:
+
+```py
+training_corpus = (
+ raw_datasets["train"][i : i + 1000]["whole_func_string"]
+ for i in range(0, len(raw_datasets["train"]), 1000)
+)
+```
+
+This line of code doesn't fetch any elements of the dataset; it just creates an object you can use in a Python `for` loop. The texts will only be loaded when you need them (that is, when you're at the step of the `for` loop that requires them), and only 1,000 texts at a time will be loaded. This way you won't exhaust all your memory even if you are processing a huge dataset.
+
+The problem with a generator object is that it can only be used once. So, instead of this giving us the list of the first 10 digits twice:
+
+```py
+gen = (i for i in range(10))
+print(list(gen))
+print(list(gen))
+```
+
+we get them once and then an empty list:
+
+```python out
+[0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
+[]
+```
+
+That's why we define a function that returns a generator instead:
+
+```py
+def get_training_corpus():
+ return (
+ raw_datasets["train"][i : i + 1000]["whole_func_string"]
+ for i in range(0, len(raw_datasets["train"]), 1000)
+ )
+
+
+training_corpus = get_training_corpus()
+```
+
+You can also define your generator inside a `for` loop by using the `yield` statement:
+
+```py
+def get_training_corpus():
+ dataset = raw_datasets["train"]
+ for start_idx in range(0, len(dataset), 1000):
+ samples = dataset[start_idx : start_idx + 1000]
+ yield samples["whole_func_string"]
+```
+
+which will produce the exact same generator as before, but allows you to use more complex logic than you can in a list comprehension.
+
+## Training a new tokenizer[[training-a-new-tokenizer]]
+
+Now that we have our corpus in the form of an iterator of batches of texts, we are ready to train a new tokenizer. To do this, we first need to load the tokenizer we want to pair with our model (here, GPT-2):
+
+```py
+from transformers import AutoTokenizer
+
+old_tokenizer = AutoTokenizer.from_pretrained("gpt2")
+```
+
+Even though we are going to train a new tokenizer, it's a good idea to do this to avoid starting entirely from scratch. This way, we won't have to specify anything about the tokenization algorithm or the special tokens we want to use; our new tokenizer will be exactly the same as GPT-2, and the only thing that will change is the vocabulary, which will be determined by the training on our corpus.
+
+First let's have a look at how this tokenizer would treat an example function:
+
+```py
+example = '''def add_numbers(a, b):
+ """Add the two numbers `a` and `b`."""
+ return a + b'''
+
+tokens = old_tokenizer.tokenize(example)
+tokens
+```
+
+```python out
+['def', 'Ġadd', '_', 'n', 'umbers', '(', 'a', ',', 'Ġb', '):', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo',
+ 'Ġnumbers', 'Ġ`', 'a', '`', 'Ġand', 'Ġ`', 'b', '`', '."', '""', 'Ċ', 'Ġ', 'Ġ', 'Ġ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
+```
+
+This tokenizer has a few special symbols, like `Ġ` and `Ċ`, which denote spaces and newlines, respectively. As we can see, this is not too efficient: the tokenizer returns individual tokens for each space, when it could group together indentation levels (since having sets of four or eight spaces is going to be very common in code). It also split the function name a bit weirdly, not being used to seeing words with the `_` character.
+
+Let's train a new tokenizer and see if it solves those issues. For this, we'll use the method `train_new_from_iterator()`:
+
+```py
+tokenizer = old_tokenizer.train_new_from_iterator(training_corpus, 52000)
+```
+
+This command might take a bit of time if your corpus is very large, but for this dataset of 1.6 GB of texts it's blazing fast (1 minute 16 seconds on an AMD Ryzen 9 3900X CPU with 12 cores).
+
+Note that `AutoTokenizer.train_new_from_iterator()` only works if the tokenizer you are using is a "fast" tokenizer. As you'll see in the next section, the 🤗 Transformers library contains two types of tokenizers: some are written purely in Python and others (the fast ones) are backed by the 🤗 Tokenizers library, which is written in the [Rust](https://www.rust-lang.org) programming language. Python is the language most often used for data science and deep learning applications, but when anything needs to be parallelized to be fast, it has to be written in another language. For instance, the matrix multiplications that are at the core of the model computation are written in CUDA, an optimized C library for GPUs.
+
+Training a brand new tokenizer in pure Python would be excruciatingly slow, which is why we developed the 🤗 Tokenizers library. Note that just as you didn't have to learn the CUDA language to be able to execute your model on a batch of inputs on a GPU, you won't need to learn Rust to use a fast tokenizer. The 🤗 Tokenizers library provides Python bindings for many methods that internally call some piece of code in Rust; for example, to parallelize the training of your new tokenizer or, as we saw in [Chapter 3](/course/chapter3), the tokenization of a batch of inputs.
+
+Most of the Transformer models have a fast tokenizer available (there are some exceptions that you can check [here](https://huggingface.co/transformers/#supported-frameworks)), and the `AutoTokenizer` API always selects the fast tokenizer for you if it's available. In the next section we'll take a look at some of the other special features fast tokenizers have, which will be really useful for tasks like token classification and question answering. Before diving into that, however, let's try our brand new tokenizer on the previous example:
+
+```py
+tokens = tokenizer.tokenize(example)
+tokens
+```
+
+```python out
+['def', 'Ġadd', '_', 'numbers', '(', 'a', ',', 'Ġb', '):', 'ĊĠĠĠ', 'Ġ"""', 'Add', 'Ġthe', 'Ġtwo', 'Ġnumbers', 'Ġ`',
+ 'a', '`', 'Ġand', 'Ġ`', 'b', '`."""', 'ĊĠĠĠ', 'Ġreturn', 'Ġa', 'Ġ+', 'Ġb']
+```
+
+Here we again see the special symbols `Ġ` and `Ċ` that denote spaces and newlines, but we can also see that our tokenizer learned some tokens that are highly specific to a corpus of Python functions: for example, there is a `ĊĠĠĠ` token that represents an indentation, and a `Ġ"""` token that represents the three quotes that start a docstring. The tokenizer also correctly split the function name on `_`. This is quite a compact representation; comparatively, using the plain English tokenizer on the same example will give us a longer sentence:
+
+```py
+print(len(tokens))
+print(len(old_tokenizer.tokenize(example)))
+```
+
+```python out
+27
+36
+```
+
+Let's look at another example:
+
+```python
+example = """class LinearLayer():
+ def __init__(self, input_size, output_size):
+ self.weight = torch.randn(input_size, output_size)
+ self.bias = torch.zeros(output_size)
+
+ def __call__(self, x):
+ return x @ self.weights + self.bias
+ """
+tokenizer.tokenize(example)
+```
+
+```python out
+['class', 'ĠLinear', 'Layer', '():', 'ĊĠĠĠ', 'Ġdef', 'Ġ__', 'init', '__(', 'self', ',', 'Ġinput', '_', 'size', ',',
+ 'Ġoutput', '_', 'size', '):', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'weight', 'Ġ=', 'Ġtorch', '.', 'randn', '(', 'input', '_',
+ 'size', ',', 'Ġoutput', '_', 'size', ')', 'ĊĠĠĠĠĠĠĠ', 'Ġself', '.', 'bias', 'Ġ=', 'Ġtorch', '.', 'zeros', '(',
+ 'output', '_', 'size', ')', 'ĊĊĠĠĠ', 'Ġdef', 'Ġ__', 'call', '__(', 'self', ',', 'Ġx', '):', 'ĊĠĠĠĠĠĠĠ',
+ 'Ġreturn', 'Ġx', 'Ġ@', 'Ġself', '.', 'weights', 'Ġ+', 'Ġself', '.', 'bias', 'ĊĠĠĠĠ']
+```
+
+In addition to the token corresponding to an indentation, here we can also see a token for a double indentation: `ĊĠĠĠĠĠĠĠ`. The special Python words like `class`, `init`, `call`, `self`, and `return` are each tokenized as one token, and we can see that as well as splitting on `_` and `.` the tokenizer correctly splits even camel-cased names: `LinearLayer` is tokenized as `["ĠLinear", "Layer"]`.
+
+## Saving the tokenizer[[saving-the-tokenizer]]
+
+To make sure we can use it later, we need to save our new tokenizer. Like for models, this is done with the `save_pretrained()` method:
+
+```py
+tokenizer.save_pretrained("code-search-net-tokenizer")
+```
+
+This will create a new folder named *code-search-net-tokenizer*, which will contain all the files the tokenizer needs to be reloaded. If you want to share this tokenizer with your colleagues and friends, you can upload it to the Hub by logging into your account. If you're working in a notebook, there's a convenience function to help you with this:
+
+```python
+from huggingface_hub import notebook_login
+
+notebook_login()
+```
+
+This will display a widget where you can enter your Hugging Face login credentials. If you aren't working in a notebook, just type the following line in your terminal:
+
+```bash
+huggingface-cli login
+```
+
+Once you've logged in, you can push your tokenizer by executing the following command:
+
+```py
+tokenizer.push_to_hub("code-search-net-tokenizer")
+```
+
+This will create a new repository in your namespace with the name `code-search-net-tokenizer`, containing the tokenizer file. You can then load the tokenizer from anywhere with the `from_pretrained()` method:
+
+```py
+# Replace "huggingface-course" below with your actual namespace to use your own tokenizer
+tokenizer = AutoTokenizer.from_pretrained("huggingface-course/code-search-net-tokenizer")
+```
+
+You're now all set for training a language model from scratch and fine-tuning it on your task at hand! We'll get to that in [Chapter 7](/course/chapter7), but first, in the rest of this chapter we'll take a closer look at fast tokenizers and explore in detail what actually happens when we call the method `train_new_from_iterator()`.
diff --git a/chapters/en/chapter6/3.mdx b/chapters/en/chapter6/3.mdx
index e3e5ee182..6bba8ba95 100644
--- a/chapters/en/chapter6/3.mdx
+++ b/chapters/en/chapter6/3.mdx
@@ -1,464 +1,464 @@
-
-
-# Fast tokenizers' special powers[[fast-tokenizers-special-powers]]
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-In this section we will take a closer look at the capabilities of the tokenizers in 🤗 Transformers. Up to now we have only used them to tokenize inputs or decode IDs back into text, but tokenizers -- especially those backed by the 🤗 Tokenizers library -- can do a lot more. To illustrate these additional features, we will explore how to reproduce the results of the `token-classification` (that we called `ner`) and `question-answering` pipelines that we first encountered in [Chapter 1](/course/chapter1).
-
-
-
-In the following discussion, we will often make the distinction between "slow" and "fast" tokenizers. Slow tokenizers are those written in Python inside the 🤗 Transformers library, while the fast versions are the ones provided by 🤗 Tokenizers, which are written in Rust. If you remember the table from [Chapter 5](/course/chapter5/3) that reported how long it took a fast and a slow tokenizer to tokenize the Drug Review Dataset, you should have an idea of why we call them fast and slow:
-
-| | Fast tokenizer | Slow tokenizer
-:--------------:|:--------------:|:-------------:
-`batched=True` | 10.8s | 4min41s
-`batched=False` | 59.2s | 5min3s
-
-> [!WARNING]
-> ⚠️ When tokenizing a single sentence, you won't always see a difference in speed between the slow and fast versions of the same tokenizer. In fact, the fast version might actually be slower! It's only when tokenizing lots of texts in parallel at the same time that you will be able to clearly see the difference.
-
-## Batch encoding[[batch-encoding]]
-
-
-
-The output of a tokenizer isn't a simple Python dictionary; what we get is actually a special `BatchEncoding` object. It's a subclass of a dictionary (which is why we were able to index into that result without any problem before), but with additional methods that are mostly used by fast tokenizers.
-
-Besides their parallelization capabilities, the key functionality of fast tokenizers is that they always keep track of the original span of texts the final tokens come from -- a feature we call *offset mapping*. This in turn unlocks features like mapping each word to the tokens it generated or mapping each character of the original text to the token it's inside, and vice versa.
-
-Let's take a look at an example:
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
-encoding = tokenizer(example)
-print(type(encoding))
-```
-
-As mentioned previously, we get a `BatchEncoding` object in the tokenizer's output:
-
-```python out
-
-```
-
-Since the `AutoTokenizer` class picks a fast tokenizer by default, we can use the additional methods this `BatchEncoding` object provides. We have two ways to check if our tokenizer is a fast or a slow one. We can either check the attribute `is_fast` of the `tokenizer`:
-
-```python
-tokenizer.is_fast
-```
-
-```python out
-True
-```
-
-or check the same attribute of our `encoding`:
-
-```python
-encoding.is_fast
-```
-
-```python out
-True
-```
-
-Let's see what a fast tokenizer enables us to do. First, we can access the tokens without having to convert the IDs back to tokens:
-
-```py
-encoding.tokens()
-```
-
-```python out
-['[CLS]', 'My', 'name', 'is', 'S', '##yl', '##va', '##in', 'and', 'I', 'work', 'at', 'Hu', '##gging', 'Face', 'in',
- 'Brooklyn', '.', '[SEP]']
-```
-
-In this case the token at index 5 is `##yl`, which is part of the word "Sylvain" in the original sentence. We can also use the `word_ids()` method to get the index of the word each token comes from:
-
-```py
-encoding.word_ids()
-```
-
-```python out
-[None, 0, 1, 2, 3, 3, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, None]
-```
-
-We can see that the tokenizer's special tokens `[CLS]` and `[SEP]` are mapped to `None`, and then each token is mapped to the word it originates from. This is especially useful to determine if a token is at the start of a word or if two tokens are in the same word. We could rely on the `##` prefix for that, but it only works for BERT-like tokenizers; this method works for any type of tokenizer as long as it's a fast one. In the next chapter, we'll see how we can use this capability to apply the labels we have for each word properly to the tokens in tasks like named entity recognition (NER) and part-of-speech (POS) tagging. We can also use it to mask all the tokens coming from the same word in masked language modeling (a technique called _whole word masking_).
-
-> [!TIP]
-> The notion of what a word is complicated. For instance, does "I'll" (a contraction of "I will") count as one or two words? It actually depends on the tokenizer and the pre-tokenization operation it applies. Some tokenizers just split on spaces, so they will consider this as one word. Others use punctuation on top of spaces, so will consider it two words.
->
-> ✏️ **Try it out!** Create a tokenizer from the `bert-base-cased` and `roberta-base` checkpoints and tokenize "81s" with them. What do you observe? What are the word IDs?
-
-Similarly, there is a `sentence_ids()` method that we can use to map a token to the sentence it came from (though in this case, the `token_type_ids` returned by the tokenizer can give us the same information).
-
-Lastly, we can map any word or token to characters in the original text, and vice versa, via the `word_to_chars()` or `token_to_chars()` and `char_to_word()` or `char_to_token()` methods. For instance, the `word_ids()` method told us that `##yl` is part of the word at index 3, but which word is it in the sentence? We can find out like this:
-
-```py
-start, end = encoding.word_to_chars(3)
-example[start:end]
-```
-
-```python out
-Sylvain
-```
-
-As we mentioned previously, this is all powered by the fact the fast tokenizer keeps track of the span of text each token comes from in a list of *offsets*. To illustrate their use, next we'll show you how to replicate the results of the `token-classification` pipeline manually.
-
-> [!TIP]
-> ✏️ **Try it out!** Create your own example text and see if you can understand which tokens are associated with word ID, and also how to extract the character spans for a single word. For bonus points, try using two sentences as input and see if the sentence IDs make sense to you.
-
-## Inside the `token-classification` pipeline[[inside-the-token-classification-pipeline]]
-
-In [Chapter 1](/course/chapter1) we got our first taste of applying NER -- where the task is to identify which parts of the text correspond to entities like persons, locations, or organizations -- with the 🤗 Transformers `pipeline()` function. Then, in [Chapter 2](/course/chapter2), we saw how a pipeline groups together the three stages necessary to get the predictions from a raw text: tokenization, passing the inputs through the model, and post-processing. The first two steps in the `token-classification` pipeline are the same as in any other pipeline, but the post-processing is a little more complex -- let's see how!
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-### Getting the base results with the pipeline[[getting-the-base-results-with-the-pipeline]]
-
-First, let's grab a token classification pipeline so we can get some results to compare manually. The model used by default is [`dbmdz/bert-large-cased-finetuned-conll03-english`](https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english); it performs NER on sentences:
-
-```py
-from transformers import pipeline
-
-token_classifier = pipeline("token-classification")
-token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
-```
-
-```python out
-[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
- {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
- {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
- {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
- {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
- {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
- {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
- {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
-```
-
-The model properly identified each token generated by "Sylvain" as a person, each token generated by "Hugging Face" as an organization, and the token "Brooklyn" as a location. We can also ask the pipeline to group together the tokens that correspond to the same entity:
-
-```py
-from transformers import pipeline
-
-token_classifier = pipeline("token-classification", aggregation_strategy="simple")
-token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
-```
-
-```python out
-[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
- {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
- {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
-```
-
-The `aggregation_strategy` picked will change the scores computed for each grouped entity. With `"simple"` the score is just the mean of the scores of each token in the given entity: for instance, the score of "Sylvain" is the mean of the scores we saw in the previous example for the tokens `S`, `##yl`, `##va`, and `##in`. Other strategies available are:
-
-- `"first"`, where the score of each entity is the score of the first token of that entity (so for "Sylvain" it would be 0.993828, the score of the token `S`)
-- `"max"`, where the score of each entity is the maximum score of the tokens in that entity (so for "Hugging Face" it would be 0.98879766, the score of "Face")
-- `"average"`, where the score of each entity is the average of the scores of the words composing that entity (so for "Sylvain" there would be no difference from the `"simple"` strategy, but "Hugging Face" would have a score of 0.9819, the average of the scores for "Hugging", 0.975, and "Face", 0.98879)
-
-Now let's see how to obtain these results without using the `pipeline()` function!
-
-### From inputs to predictions[[from-inputs-to-predictions]]
-
-{#if fw === 'pt'}
-
-First we need to tokenize our input and pass it through the model. This is done exactly as in [Chapter 2](/course/chapter2); we instantiate the tokenizer and the model using the `AutoXxx` classes and then use them on our example:
-
-```py
-from transformers import AutoTokenizer, AutoModelForTokenClassification
-
-model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-model = AutoModelForTokenClassification.from_pretrained(model_checkpoint)
-
-example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
-inputs = tokenizer(example, return_tensors="pt")
-outputs = model(**inputs)
-```
-
-Since we're using `AutoModelForTokenClassification` here, we get one set of logits for each token in the input sequence:
-
-```py
-print(inputs["input_ids"].shape)
-print(outputs.logits.shape)
-```
-
-```python out
-torch.Size([1, 19])
-torch.Size([1, 19, 9])
-```
-
-{:else}
-
-First we need to tokenize our input and pass it through the model. This is done exactly as in [Chapter 2](/course/chapter2); we instantiate the tokenizer and the model using the `TFAutoXxx` classes and then use them on our example:
-
-```py
-from transformers import AutoTokenizer, TFAutoModelForTokenClassification
-
-model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-model = TFAutoModelForTokenClassification.from_pretrained(model_checkpoint)
-
-example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
-inputs = tokenizer(example, return_tensors="tf")
-outputs = model(**inputs)
-```
-
-Since we're using `TFAutoModelForTokenClassification` here, we get one set of logits for each token in the input sequence:
-
-```py
-print(inputs["input_ids"].shape)
-print(outputs.logits.shape)
-```
-
-```python out
-(1, 19)
-(1, 19, 9)
-```
-
-{/if}
-
-We have a batch with 1 sequence of 19 tokens and the model has 9 different labels, so the output of the model has a shape of 1 x 19 x 9. Like for the text classification pipeline, we use a softmax function to convert those logits to probabilities, and we take the argmax to get predictions (note that we can take the argmax on the logits because the softmax does not change the order):
-
-{#if fw === 'pt'}
-
-```py
-import torch
-
-probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)[0].tolist()
-predictions = outputs.logits.argmax(dim=-1)[0].tolist()
-print(predictions)
-```
-
-{:else}
-
-```py
-import tensorflow as tf
-
-probabilities = tf.math.softmax(outputs.logits, axis=-1)[0]
-probabilities = probabilities.numpy().tolist()
-predictions = tf.math.argmax(outputs.logits, axis=-1)[0]
-predictions = predictions.numpy().tolist()
-print(predictions)
-```
-
-{/if}
-
-```python out
-[0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0]
-```
-
-The `model.config.id2label` attribute contains the mapping of indexes to labels that we can use to make sense of the predictions:
-
-```py
-model.config.id2label
-```
-
-```python out
-{0: 'O',
- 1: 'B-MISC',
- 2: 'I-MISC',
- 3: 'B-PER',
- 4: 'I-PER',
- 5: 'B-ORG',
- 6: 'I-ORG',
- 7: 'B-LOC',
- 8: 'I-LOC'}
-```
-
-As we saw earlier, there are 9 labels: `O` is the label for the tokens that are not in any named entity (it stands for "outside"), and we then have two labels for each type of entity (miscellaneous, person, organization, and location). The label `B-XXX` indicates the token is at the beginning of an entity `XXX` and the label `I-XXX` indicates the token is inside the entity `XXX`. For instance, in the current example we would expect our model to classify the token `S` as `B-PER` (beginning of a person entity) and the tokens `##yl`, `##va` and `##in` as `I-PER` (inside a person entity).
-
-You might think the model was wrong in this case as it gave the label `I-PER` to all four of these tokens, but that's not entirely true. There are actually two formats for those `B-` and `I-` labels: *IOB1* and *IOB2*. The IOB2 format (in pink below), is the one we introduced whereas in the IOB1 format (in blue), the labels beginning with `B-` are only ever used to separate two adjacent entities of the same type. The model we are using was fine-tuned on a dataset using that format, which is why it assigns the label `I-PER` to the `S` token.
-
-
-

-

-
-
-With this map, we are ready to reproduce (almost entirely) the results of the first pipeline -- we can just grab the score and label of each token that was not classified as `O`:
-
-```py
-results = []
-tokens = inputs.tokens()
-
-for idx, pred in enumerate(predictions):
- label = model.config.id2label[pred]
- if label != "O":
- results.append(
- {"entity": label, "score": probabilities[idx][pred], "word": tokens[idx]}
- )
-
-print(results)
-```
-
-```python out
-[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S'},
- {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl'},
- {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va'},
- {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in'},
- {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu'},
- {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging'},
- {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face'},
- {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn'}]
-```
-
-This is very similar to what we had before, with one exception: the pipeline also gave us information about the `start` and `end` of each entity in the original sentence. This is where our offset mapping will come into play. To get the offsets, we just have to set `return_offsets_mapping=True` when we apply the tokenizer to our inputs:
-
-```py
-inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
-inputs_with_offsets["offset_mapping"]
-```
-
-```python out
-[(0, 0), (0, 2), (3, 7), (8, 10), (11, 12), (12, 14), (14, 16), (16, 18), (19, 22), (23, 24), (25, 29), (30, 32),
- (33, 35), (35, 40), (41, 45), (46, 48), (49, 57), (57, 58), (0, 0)]
-```
-
-Each tuple is the span of text corresponding to each token, where `(0, 0)` is reserved for the special tokens. We saw before that the token at index 5 is `##yl`, which has `(12, 14)` as offsets here. If we grab the corresponding slice in our example:
-
-
-```py
-example[12:14]
-```
-
-we get the proper span of text without the `##`:
-
-```python out
-yl
-```
-
-Using this, we can now complete the previous results:
-
-```py
-results = []
-inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
-tokens = inputs_with_offsets.tokens()
-offsets = inputs_with_offsets["offset_mapping"]
-
-for idx, pred in enumerate(predictions):
- label = model.config.id2label[pred]
- if label != "O":
- start, end = offsets[idx]
- results.append(
- {
- "entity": label,
- "score": probabilities[idx][pred],
- "word": tokens[idx],
- "start": start,
- "end": end,
- }
- )
-
-print(results)
-```
-
-```python out
-[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
- {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
- {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
- {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
- {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
- {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
- {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
- {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
-```
-
-This is the same as what we got from the first pipeline!
-
-### Grouping entities[[grouping-entities]]
-
-Using the offsets to determine the start and end keys for each entity is handy, but that information isn't strictly necessary. When we want to group the entities together, however, the offsets will save us a lot of messy code. For example, if we wanted to group together the tokens `Hu`, `##gging`, and `Face`, we could make special rules that say the first two should be attached while removing the `##`, and the `Face` should be added with a space since it does not begin with `##` -- but that would only work for this particular type of tokenizer. We would have to write another set of rules for a SentencePiece or a Byte-Pair-Encoding tokenizer (discussed later in this chapter).
-
-With the offsets, all that custom code goes away: we just can take the span in the original text that begins with the first token and ends with the last token. So, in the case of the tokens `Hu`, `##gging`, and `Face`, we should start at character 33 (the beginning of `Hu`) and end before character 45 (the end of `Face`):
-
-```py
-example[33:45]
-```
-
-```python out
-Hugging Face
-```
-
-To write the code that post-processes the predictions while grouping entities, we will group together entities that are consecutive and labeled with `I-XXX`, except for the first one, which can be labeled as `B-XXX` or `I-XXX` (so, we stop grouping an entity when we get a `O`, a new type of entity, or a `B-XXX` that tells us an entity of the same type is starting):
-
-```py
-import numpy as np
-
-results = []
-inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
-tokens = inputs_with_offsets.tokens()
-offsets = inputs_with_offsets["offset_mapping"]
-
-idx = 0
-while idx < len(predictions):
- pred = predictions[idx]
- label = model.config.id2label[pred]
- if label != "O":
- # Remove the B- or I-
- label = label[2:]
- start, _ = offsets[idx]
-
- # Grab all the tokens labeled with I-label
- all_scores = []
- while (
- idx < len(predictions)
- and model.config.id2label[predictions[idx]] == f"I-{label}"
- ):
- all_scores.append(probabilities[idx][pred])
- _, end = offsets[idx]
- idx += 1
-
- # The score is the mean of all the scores of the tokens in that grouped entity
- score = np.mean(all_scores).item()
- word = example[start:end]
- results.append(
- {
- "entity_group": label,
- "score": score,
- "word": word,
- "start": start,
- "end": end,
- }
- )
- idx += 1
-
-print(results)
-```
-
-And we get the same results as with our second pipeline!
-
-```python out
-[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
- {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
- {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
-```
-
-Another example of a task where these offsets are extremely useful is question answering. Diving into that pipeline, which we'll do in the next section, will also enable us to take a look at one last feature of the tokenizers in the 🤗 Transformers library: dealing with overflowing tokens when we truncate an input to a given length.
+
+
+# Fast tokenizers' special powers[[fast-tokenizers-special-powers]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+In this section we will take a closer look at the capabilities of the tokenizers in 🤗 Transformers. Up to now we have only used them to tokenize inputs or decode IDs back into text, but tokenizers -- especially those backed by the 🤗 Tokenizers library -- can do a lot more. To illustrate these additional features, we will explore how to reproduce the results of the `token-classification` (that we called `ner`) and `question-answering` pipelines that we first encountered in [Chapter 1](/course/chapter1).
+
+
+
+In the following discussion, we will often make the distinction between "slow" and "fast" tokenizers. Slow tokenizers are those written in Python inside the 🤗 Transformers library, while the fast versions are the ones provided by 🤗 Tokenizers, which are written in Rust. If you remember the table from [Chapter 5](/course/chapter5/3) that reported how long it took a fast and a slow tokenizer to tokenize the Drug Review Dataset, you should have an idea of why we call them fast and slow:
+
+| | Fast tokenizer | Slow tokenizer
+:--------------:|:--------------:|:-------------:
+`batched=True` | 10.8s | 4min41s
+`batched=False` | 59.2s | 5min3s
+
+> [!WARNING]
+> ⚠️ When tokenizing a single sentence, you won't always see a difference in speed between the slow and fast versions of the same tokenizer. In fact, the fast version might actually be slower! It's only when tokenizing lots of texts in parallel at the same time that you will be able to clearly see the difference.
+
+## Batch encoding[[batch-encoding]]
+
+
+
+The output of a tokenizer isn't a simple Python dictionary; what we get is actually a special `BatchEncoding` object. It's a subclass of a dictionary (which is why we were able to index into that result without any problem before), but with additional methods that are mostly used by fast tokenizers.
+
+Besides their parallelization capabilities, the key functionality of fast tokenizers is that they always keep track of the original span of texts the final tokens come from -- a feature we call *offset mapping*. This in turn unlocks features like mapping each word to the tokens it generated or mapping each character of the original text to the token it's inside, and vice versa.
+
+Let's take a look at an example:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
+encoding = tokenizer(example)
+print(type(encoding))
+```
+
+As mentioned previously, we get a `BatchEncoding` object in the tokenizer's output:
+
+```python out
+
+```
+
+Since the `AutoTokenizer` class picks a fast tokenizer by default, we can use the additional methods this `BatchEncoding` object provides. We have two ways to check if our tokenizer is a fast or a slow one. We can either check the attribute `is_fast` of the `tokenizer`:
+
+```python
+tokenizer.is_fast
+```
+
+```python out
+True
+```
+
+or check the same attribute of our `encoding`:
+
+```python
+encoding.is_fast
+```
+
+```python out
+True
+```
+
+Let's see what a fast tokenizer enables us to do. First, we can access the tokens without having to convert the IDs back to tokens:
+
+```py
+encoding.tokens()
+```
+
+```python out
+['[CLS]', 'My', 'name', 'is', 'S', '##yl', '##va', '##in', 'and', 'I', 'work', 'at', 'Hu', '##gging', 'Face', 'in',
+ 'Brooklyn', '.', '[SEP]']
+```
+
+In this case the token at index 5 is `##yl`, which is part of the word "Sylvain" in the original sentence. We can also use the `word_ids()` method to get the index of the word each token comes from:
+
+```py
+encoding.word_ids()
+```
+
+```python out
+[None, 0, 1, 2, 3, 3, 3, 3, 4, 5, 6, 7, 8, 8, 9, 10, 11, 12, None]
+```
+
+We can see that the tokenizer's special tokens `[CLS]` and `[SEP]` are mapped to `None`, and then each token is mapped to the word it originates from. This is especially useful to determine if a token is at the start of a word or if two tokens are in the same word. We could rely on the `##` prefix for that, but it only works for BERT-like tokenizers; this method works for any type of tokenizer as long as it's a fast one. In the next chapter, we'll see how we can use this capability to apply the labels we have for each word properly to the tokens in tasks like named entity recognition (NER) and part-of-speech (POS) tagging. We can also use it to mask all the tokens coming from the same word in masked language modeling (a technique called _whole word masking_).
+
+> [!TIP]
+> The notion of what a word is complicated. For instance, does "I'll" (a contraction of "I will") count as one or two words? It actually depends on the tokenizer and the pre-tokenization operation it applies. Some tokenizers just split on spaces, so they will consider this as one word. Others use punctuation on top of spaces, so will consider it two words.
+>
+> ✏️ **Try it out!** Create a tokenizer from the `bert-base-cased` and `roberta-base` checkpoints and tokenize "81s" with them. What do you observe? What are the word IDs?
+
+Similarly, there is a `sentence_ids()` method that we can use to map a token to the sentence it came from (though in this case, the `token_type_ids` returned by the tokenizer can give us the same information).
+
+Lastly, we can map any word or token to characters in the original text, and vice versa, via the `word_to_chars()` or `token_to_chars()` and `char_to_word()` or `char_to_token()` methods. For instance, the `word_ids()` method told us that `##yl` is part of the word at index 3, but which word is it in the sentence? We can find out like this:
+
+```py
+start, end = encoding.word_to_chars(3)
+example[start:end]
+```
+
+```python out
+Sylvain
+```
+
+As we mentioned previously, this is all powered by the fact the fast tokenizer keeps track of the span of text each token comes from in a list of *offsets*. To illustrate their use, next we'll show you how to replicate the results of the `token-classification` pipeline manually.
+
+> [!TIP]
+> ✏️ **Try it out!** Create your own example text and see if you can understand which tokens are associated with word ID, and also how to extract the character spans for a single word. For bonus points, try using two sentences as input and see if the sentence IDs make sense to you.
+
+## Inside the `token-classification` pipeline[[inside-the-token-classification-pipeline]]
+
+In [Chapter 1](/course/chapter1) we got our first taste of applying NER -- where the task is to identify which parts of the text correspond to entities like persons, locations, or organizations -- with the 🤗 Transformers `pipeline()` function. Then, in [Chapter 2](/course/chapter2), we saw how a pipeline groups together the three stages necessary to get the predictions from a raw text: tokenization, passing the inputs through the model, and post-processing. The first two steps in the `token-classification` pipeline are the same as in any other pipeline, but the post-processing is a little more complex -- let's see how!
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+### Getting the base results with the pipeline[[getting-the-base-results-with-the-pipeline]]
+
+First, let's grab a token classification pipeline so we can get some results to compare manually. The model used by default is [`dbmdz/bert-large-cased-finetuned-conll03-english`](https://huggingface.co/dbmdz/bert-large-cased-finetuned-conll03-english); it performs NER on sentences:
+
+```py
+from transformers import pipeline
+
+token_classifier = pipeline("token-classification")
+token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+```python out
+[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
+ {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
+ {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
+ {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
+ {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
+ {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
+ {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
+ {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+The model properly identified each token generated by "Sylvain" as a person, each token generated by "Hugging Face" as an organization, and the token "Brooklyn" as a location. We can also ask the pipeline to group together the tokens that correspond to the same entity:
+
+```py
+from transformers import pipeline
+
+token_classifier = pipeline("token-classification", aggregation_strategy="simple")
+token_classifier("My name is Sylvain and I work at Hugging Face in Brooklyn.")
+```
+
+```python out
+[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
+ {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
+ {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+The `aggregation_strategy` picked will change the scores computed for each grouped entity. With `"simple"` the score is just the mean of the scores of each token in the given entity: for instance, the score of "Sylvain" is the mean of the scores we saw in the previous example for the tokens `S`, `##yl`, `##va`, and `##in`. Other strategies available are:
+
+- `"first"`, where the score of each entity is the score of the first token of that entity (so for "Sylvain" it would be 0.993828, the score of the token `S`)
+- `"max"`, where the score of each entity is the maximum score of the tokens in that entity (so for "Hugging Face" it would be 0.98879766, the score of "Face")
+- `"average"`, where the score of each entity is the average of the scores of the words composing that entity (so for "Sylvain" there would be no difference from the `"simple"` strategy, but "Hugging Face" would have a score of 0.9819, the average of the scores for "Hugging", 0.975, and "Face", 0.98879)
+
+Now let's see how to obtain these results without using the `pipeline()` function!
+
+### From inputs to predictions[[from-inputs-to-predictions]]
+
+{#if fw === 'pt'}
+
+First we need to tokenize our input and pass it through the model. This is done exactly as in [Chapter 2](/course/chapter2); we instantiate the tokenizer and the model using the `AutoXxx` classes and then use them on our example:
+
+```py
+from transformers import AutoTokenizer, AutoModelForTokenClassification
+
+model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = AutoModelForTokenClassification.from_pretrained(model_checkpoint)
+
+example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
+inputs = tokenizer(example, return_tensors="pt")
+outputs = model(**inputs)
+```
+
+Since we're using `AutoModelForTokenClassification` here, we get one set of logits for each token in the input sequence:
+
+```py
+print(inputs["input_ids"].shape)
+print(outputs.logits.shape)
+```
+
+```python out
+torch.Size([1, 19])
+torch.Size([1, 19, 9])
+```
+
+{:else}
+
+First we need to tokenize our input and pass it through the model. This is done exactly as in [Chapter 2](/course/chapter2); we instantiate the tokenizer and the model using the `TFAutoXxx` classes and then use them on our example:
+
+```py
+from transformers import AutoTokenizer, TFAutoModelForTokenClassification
+
+model_checkpoint = "dbmdz/bert-large-cased-finetuned-conll03-english"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = TFAutoModelForTokenClassification.from_pretrained(model_checkpoint)
+
+example = "My name is Sylvain and I work at Hugging Face in Brooklyn."
+inputs = tokenizer(example, return_tensors="tf")
+outputs = model(**inputs)
+```
+
+Since we're using `TFAutoModelForTokenClassification` here, we get one set of logits for each token in the input sequence:
+
+```py
+print(inputs["input_ids"].shape)
+print(outputs.logits.shape)
+```
+
+```python out
+(1, 19)
+(1, 19, 9)
+```
+
+{/if}
+
+We have a batch with 1 sequence of 19 tokens and the model has 9 different labels, so the output of the model has a shape of 1 x 19 x 9. Like for the text classification pipeline, we use a softmax function to convert those logits to probabilities, and we take the argmax to get predictions (note that we can take the argmax on the logits because the softmax does not change the order):
+
+{#if fw === 'pt'}
+
+```py
+import torch
+
+probabilities = torch.nn.functional.softmax(outputs.logits, dim=-1)[0].tolist()
+predictions = outputs.logits.argmax(dim=-1)[0].tolist()
+print(predictions)
+```
+
+{:else}
+
+```py
+import tensorflow as tf
+
+probabilities = tf.math.softmax(outputs.logits, axis=-1)[0]
+probabilities = probabilities.numpy().tolist()
+predictions = tf.math.argmax(outputs.logits, axis=-1)[0]
+predictions = predictions.numpy().tolist()
+print(predictions)
+```
+
+{/if}
+
+```python out
+[0, 0, 0, 0, 4, 4, 4, 4, 0, 0, 0, 0, 6, 6, 6, 0, 8, 0, 0]
+```
+
+The `model.config.id2label` attribute contains the mapping of indexes to labels that we can use to make sense of the predictions:
+
+```py
+model.config.id2label
+```
+
+```python out
+{0: 'O',
+ 1: 'B-MISC',
+ 2: 'I-MISC',
+ 3: 'B-PER',
+ 4: 'I-PER',
+ 5: 'B-ORG',
+ 6: 'I-ORG',
+ 7: 'B-LOC',
+ 8: 'I-LOC'}
+```
+
+As we saw earlier, there are 9 labels: `O` is the label for the tokens that are not in any named entity (it stands for "outside"), and we then have two labels for each type of entity (miscellaneous, person, organization, and location). The label `B-XXX` indicates the token is at the beginning of an entity `XXX` and the label `I-XXX` indicates the token is inside the entity `XXX`. For instance, in the current example we would expect our model to classify the token `S` as `B-PER` (beginning of a person entity) and the tokens `##yl`, `##va` and `##in` as `I-PER` (inside a person entity).
+
+You might think the model was wrong in this case as it gave the label `I-PER` to all four of these tokens, but that's not entirely true. There are actually two formats for those `B-` and `I-` labels: *IOB1* and *IOB2*. The IOB2 format (in pink below), is the one we introduced whereas in the IOB1 format (in blue), the labels beginning with `B-` are only ever used to separate two adjacent entities of the same type. The model we are using was fine-tuned on a dataset using that format, which is why it assigns the label `I-PER` to the `S` token.
+
+
+

+

+
+
+With this map, we are ready to reproduce (almost entirely) the results of the first pipeline -- we can just grab the score and label of each token that was not classified as `O`:
+
+```py
+results = []
+tokens = inputs.tokens()
+
+for idx, pred in enumerate(predictions):
+ label = model.config.id2label[pred]
+ if label != "O":
+ results.append(
+ {"entity": label, "score": probabilities[idx][pred], "word": tokens[idx]}
+ )
+
+print(results)
+```
+
+```python out
+[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S'},
+ {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl'},
+ {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va'},
+ {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in'},
+ {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu'},
+ {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging'},
+ {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face'},
+ {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn'}]
+```
+
+This is very similar to what we had before, with one exception: the pipeline also gave us information about the `start` and `end` of each entity in the original sentence. This is where our offset mapping will come into play. To get the offsets, we just have to set `return_offsets_mapping=True` when we apply the tokenizer to our inputs:
+
+```py
+inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
+inputs_with_offsets["offset_mapping"]
+```
+
+```python out
+[(0, 0), (0, 2), (3, 7), (8, 10), (11, 12), (12, 14), (14, 16), (16, 18), (19, 22), (23, 24), (25, 29), (30, 32),
+ (33, 35), (35, 40), (41, 45), (46, 48), (49, 57), (57, 58), (0, 0)]
+```
+
+Each tuple is the span of text corresponding to each token, where `(0, 0)` is reserved for the special tokens. We saw before that the token at index 5 is `##yl`, which has `(12, 14)` as offsets here. If we grab the corresponding slice in our example:
+
+
+```py
+example[12:14]
+```
+
+we get the proper span of text without the `##`:
+
+```python out
+yl
+```
+
+Using this, we can now complete the previous results:
+
+```py
+results = []
+inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
+tokens = inputs_with_offsets.tokens()
+offsets = inputs_with_offsets["offset_mapping"]
+
+for idx, pred in enumerate(predictions):
+ label = model.config.id2label[pred]
+ if label != "O":
+ start, end = offsets[idx]
+ results.append(
+ {
+ "entity": label,
+ "score": probabilities[idx][pred],
+ "word": tokens[idx],
+ "start": start,
+ "end": end,
+ }
+ )
+
+print(results)
+```
+
+```python out
+[{'entity': 'I-PER', 'score': 0.9993828, 'index': 4, 'word': 'S', 'start': 11, 'end': 12},
+ {'entity': 'I-PER', 'score': 0.99815476, 'index': 5, 'word': '##yl', 'start': 12, 'end': 14},
+ {'entity': 'I-PER', 'score': 0.99590725, 'index': 6, 'word': '##va', 'start': 14, 'end': 16},
+ {'entity': 'I-PER', 'score': 0.9992327, 'index': 7, 'word': '##in', 'start': 16, 'end': 18},
+ {'entity': 'I-ORG', 'score': 0.97389334, 'index': 12, 'word': 'Hu', 'start': 33, 'end': 35},
+ {'entity': 'I-ORG', 'score': 0.976115, 'index': 13, 'word': '##gging', 'start': 35, 'end': 40},
+ {'entity': 'I-ORG', 'score': 0.98879766, 'index': 14, 'word': 'Face', 'start': 41, 'end': 45},
+ {'entity': 'I-LOC', 'score': 0.99321055, 'index': 16, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+This is the same as what we got from the first pipeline!
+
+### Grouping entities[[grouping-entities]]
+
+Using the offsets to determine the start and end keys for each entity is handy, but that information isn't strictly necessary. When we want to group the entities together, however, the offsets will save us a lot of messy code. For example, if we wanted to group together the tokens `Hu`, `##gging`, and `Face`, we could make special rules that say the first two should be attached while removing the `##`, and the `Face` should be added with a space since it does not begin with `##` -- but that would only work for this particular type of tokenizer. We would have to write another set of rules for a SentencePiece or a Byte-Pair-Encoding tokenizer (discussed later in this chapter).
+
+With the offsets, all that custom code goes away: we just can take the span in the original text that begins with the first token and ends with the last token. So, in the case of the tokens `Hu`, `##gging`, and `Face`, we should start at character 33 (the beginning of `Hu`) and end before character 45 (the end of `Face`):
+
+```py
+example[33:45]
+```
+
+```python out
+Hugging Face
+```
+
+To write the code that post-processes the predictions while grouping entities, we will group together entities that are consecutive and labeled with `I-XXX`, except for the first one, which can be labeled as `B-XXX` or `I-XXX` (so, we stop grouping an entity when we get a `O`, a new type of entity, or a `B-XXX` that tells us an entity of the same type is starting):
+
+```py
+import numpy as np
+
+results = []
+inputs_with_offsets = tokenizer(example, return_offsets_mapping=True)
+tokens = inputs_with_offsets.tokens()
+offsets = inputs_with_offsets["offset_mapping"]
+
+idx = 0
+while idx < len(predictions):
+ pred = predictions[idx]
+ label = model.config.id2label[pred]
+ if label != "O":
+ # Remove the B- or I-
+ label = label[2:]
+ start, _ = offsets[idx]
+
+ # Grab all the tokens labeled with I-label
+ all_scores = []
+ while (
+ idx < len(predictions)
+ and model.config.id2label[predictions[idx]] == f"I-{label}"
+ ):
+ all_scores.append(probabilities[idx][pred])
+ _, end = offsets[idx]
+ idx += 1
+
+ # The score is the mean of all the scores of the tokens in that grouped entity
+ score = np.mean(all_scores).item()
+ word = example[start:end]
+ results.append(
+ {
+ "entity_group": label,
+ "score": score,
+ "word": word,
+ "start": start,
+ "end": end,
+ }
+ )
+ idx += 1
+
+print(results)
+```
+
+And we get the same results as with our second pipeline!
+
+```python out
+[{'entity_group': 'PER', 'score': 0.9981694, 'word': 'Sylvain', 'start': 11, 'end': 18},
+ {'entity_group': 'ORG', 'score': 0.97960204, 'word': 'Hugging Face', 'start': 33, 'end': 45},
+ {'entity_group': 'LOC', 'score': 0.99321055, 'word': 'Brooklyn', 'start': 49, 'end': 57}]
+```
+
+Another example of a task where these offsets are extremely useful is question answering. Diving into that pipeline, which we'll do in the next section, will also enable us to take a look at one last feature of the tokenizers in the 🤗 Transformers library: dealing with overflowing tokens when we truncate an input to a given length.
diff --git a/chapters/en/chapter6/3b.mdx b/chapters/en/chapter6/3b.mdx
index 4fbdea5c1..052e254fe 100644
--- a/chapters/en/chapter6/3b.mdx
+++ b/chapters/en/chapter6/3b.mdx
@@ -1,630 +1,630 @@
-
-
-# Fast tokenizers in the QA pipeline[[fast-tokenizers-in-the-qa-pipeline]]
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-We will now dive into the `question-answering` pipeline and see how to leverage the offsets to grab the answer to the question at hand from the context, a bit like we did for the grouped entities in the previous section. Then we will see how we can deal with very long contexts that end up being truncated. You can skip this section if you're not interested in the question answering task.
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-## Using the `question-answering` pipeline[[using-the-question-answering-pipeline]]
-
-As we saw in [Chapter 1](/course/chapter1), we can use the `question-answering` pipeline like this to get the answer to a question:
-
-```py
-from transformers import pipeline
-
-question_answerer = pipeline("question-answering")
-context = """
-🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch, and TensorFlow — with a seamless integration
-between them. It's straightforward to train your models with one before loading them for inference with the other.
-"""
-question = "Which deep learning libraries back 🤗 Transformers?"
-question_answerer(question=question, context=context)
-```
-
-```python out
-{'score': 0.97773,
- 'start': 78,
- 'end': 105,
- 'answer': 'Jax, PyTorch and TensorFlow'}
-```
-
-Unlike the other pipelines, which can't truncate and split texts that are longer than the maximum length accepted by the model (and thus may miss information at the end of a document), this pipeline can deal with very long contexts and will return the answer to the question even if it's at the end:
-
-```py
-long_context = """
-🤗 Transformers: State of the Art NLP
-
-🤗 Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
-question answering, summarization, translation, text generation and more in over 100 languages.
-Its aim is to make cutting-edge NLP easier to use for everyone.
-
-🤗 Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
-then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
-can be modified to enable quick research experiments.
-
-Why should I use transformers?
-
-1. Easy-to-use state-of-the-art models:
- - High performance on NLU and NLG tasks.
- - Low barrier to entry for educators and practitioners.
- - Few user-facing abstractions with just three classes to learn.
- - A unified API for using all our pretrained models.
- - Lower compute costs, smaller carbon footprint:
-
-2. Researchers can share trained models instead of always retraining.
- - Practitioners can reduce compute time and production costs.
- - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.
-
-3. Choose the right framework for every part of a model's lifetime:
- - Train state-of-the-art models in 3 lines of code.
- - Move a single model between TF2.0/PyTorch frameworks at will.
- - Seamlessly pick the right framework for training, evaluation and production.
-
-4. Easily customize a model or an example to your needs:
- - We provide examples for each architecture to reproduce the results published by its original authors.
- - Model internals are exposed as consistently as possible.
- - Model files can be used independently of the library for quick experiments.
-
-🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration
-between them. It's straightforward to train your models with one before loading them for inference with the other.
-"""
-question_answerer(question=question, context=long_context)
-```
-
-```python out
-{'score': 0.97149,
- 'start': 1892,
- 'end': 1919,
- 'answer': 'Jax, PyTorch and TensorFlow'}
-```
-
-Let's see how it does all of this!
-
-## Using a model for question answering[[using-a-model-for-question-answering]]
-
-Like with any other pipeline, we start by tokenizing our input and then send it through the model. The checkpoint used by default for the `question-answering` pipeline is [`distilbert-base-cased-distilled-squad`](https://huggingface.co/distilbert-base-cased-distilled-squad) (the "squad" in the name comes from the dataset on which the model was fine-tuned; we'll talk more about the SQuAD dataset in [Chapter 7](/course/chapter7/7)):
-
-{#if fw === 'pt'}
-
-```py
-from transformers import AutoTokenizer, AutoModelForQuestionAnswering
-
-model_checkpoint = "distilbert-base-cased-distilled-squad"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
-
-inputs = tokenizer(question, context, return_tensors="pt")
-outputs = model(**inputs)
-```
-
-{:else}
-
-```py
-from transformers import AutoTokenizer, TFAutoModelForQuestionAnswering
-
-model_checkpoint = "distilbert-base-cased-distilled-squad"
-tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
-model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
-
-inputs = tokenizer(question, context, return_tensors="tf")
-outputs = model(**inputs)
-```
-
-{/if}
-
-Note that we tokenize the question and the context as a pair, with the question first.
-
-
-

-

-
-
-Models for question answering work a little differently from the models we've seen up to now. Using the picture above as an example, the model has been trained to predict the index of the token starting the answer (here 21) and the index of the token where the answer ends (here 24). This is why those models don't return one tensor of logits but two: one for the logits corresponding to the start token of the answer, and one for the logits corresponding to the end token of the answer. Since in this case we have only one input containing 66 tokens, we get:
-
-```py
-start_logits = outputs.start_logits
-end_logits = outputs.end_logits
-print(start_logits.shape, end_logits.shape)
-```
-
-{#if fw === 'pt'}
-
-```python out
-torch.Size([1, 66]) torch.Size([1, 66])
-```
-
-{:else}
-
-```python out
-(1, 66) (1, 66)
-```
-
-{/if}
-
-To convert those logits into probabilities, we will apply a softmax function -- but before that, we need to make sure we mask the indices that are not part of the context. Our input is `[CLS] question [SEP] context [SEP]`, so we need to mask the tokens of the question as well as the `[SEP]` token. We'll keep the `[CLS]` token, however, as some models use it to indicate that the answer is not in the context.
-
-Since we will apply a softmax afterward, we just need to replace the logits we want to mask with a large negative number. Here, we use `-10000`:
-
-{#if fw === 'pt'}
-
-```py
-import torch
-
-sequence_ids = inputs.sequence_ids()
-# Mask everything apart from the tokens of the context
-mask = [i != 1 for i in sequence_ids]
-# Unmask the [CLS] token
-mask[0] = False
-mask = torch.tensor(mask)[None]
-
-start_logits[mask] = -10000
-end_logits[mask] = -10000
-```
-
-{:else}
-
-```py
-import tensorflow as tf
-
-sequence_ids = inputs.sequence_ids()
-# Mask everything apart from the tokens of the context
-mask = [i != 1 for i in sequence_ids]
-# Unmask the [CLS] token
-mask[0] = False
-mask = tf.constant(mask)[None]
-
-start_logits = tf.where(mask, -10000, start_logits)
-end_logits = tf.where(mask, -10000, end_logits)
-```
-
-{/if}
-
-Now that we have properly masked the logits corresponding to positions we don't want to predict, we can apply the softmax:
-
-{#if fw === 'pt'}
-
-```py
-start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)[0]
-end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)[0]
-```
-
-{:else}
-
-```py
-start_probabilities = tf.math.softmax(start_logits, axis=-1)[0].numpy()
-end_probabilities = tf.math.softmax(end_logits, axis=-1)[0].numpy()
-```
-
-{/if}
-
-At this stage, we could take the argmax of the start and end probabilities -- but we might end up with a start index that is greater than the end index, so we need to take a few more precautions. We will compute the probabilities of each possible `start_index` and `end_index` where `start_index <= end_index`, then take the tuple `(start_index, end_index)` with the highest probability.
-
-Assuming the events "The answer starts at `start_index`" and "The answer ends at `end_index`" to be independent, the probability that the answer starts at `start_index` and ends at `end_index` is:
-
-$$\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]$$
-
-So, to compute all the scores, we just need to compute all the products \\(\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]\\) where `start_index <= end_index`.
-
-First let's compute all the possible products:
-
-```py
-scores = start_probabilities[:, None] * end_probabilities[None, :]
-```
-
-{#if fw === 'pt'}
-
-Then we'll mask the values where `start_index > end_index` by setting them to `0` (the other probabilities are all positive numbers). The `torch.triu()` function returns the upper triangular part of the 2D tensor passed as an argument, so it will do that masking for us:
-
-```py
-scores = torch.triu(scores)
-```
-
-{:else}
-
-Then we'll mask the values where `start_index > end_index` by setting them to `0` (the other probabilities are all positive numbers). The `np.triu()` function returns the upper triangular part of the 2D tensor passed as an argument, so it will do that masking for us:
-
-```py
-import numpy as np
-
-scores = np.triu(scores)
-```
-
-{/if}
-
-Now we just have to get the index of the maximum. Since PyTorch will return the index in the flattened tensor, we need to use the floor division `//` and modulus `%` operations to get the `start_index` and `end_index`:
-
-```py
-max_index = scores.argmax().item()
-start_index = max_index // scores.shape[1]
-end_index = max_index % scores.shape[1]
-print(scores[start_index, end_index])
-```
-
-We're not quite done yet, but at least we already have the correct score for the answer (you can check this by comparing it to the first result in the previous section):
-
-```python out
-0.97773
-```
-
-> [!TIP]
-> ✏️ **Try it out!** Compute the start and end indices for the five most likely answers.
-
-We have the `start_index` and `end_index` of the answer in terms of tokens, so now we just need to convert to the character indices in the context. This is where the offsets will be super useful. We can grab them and use them like we did in the token classification task:
-
-```py
-inputs_with_offsets = tokenizer(question, context, return_offsets_mapping=True)
-offsets = inputs_with_offsets["offset_mapping"]
-
-start_char, _ = offsets[start_index]
-_, end_char = offsets[end_index]
-answer = context[start_char:end_char]
-```
-
-Now we just have to format everything to get our result:
-
-```py
-result = {
- "answer": answer,
- "start": start_char,
- "end": end_char,
- "score": scores[start_index, end_index],
-}
-print(result)
-```
-
-```python out
-{'answer': 'Jax, PyTorch and TensorFlow',
- 'start': 78,
- 'end': 105,
- 'score': 0.97773}
-```
-
-Great! That's the same as in our first example!
-
-> [!TIP]
-> ✏️ **Try it out!** Use the best scores you computed earlier to show the five most likely answers. To check your results, go back to the first pipeline and pass in `top_k=5` when calling it.
-
-## Handling long contexts[[handling-long-contexts]]
-
-If we try to tokenize the question and long context we used as an example previously, we'll get a number of tokens higher than the maximum length used in the `question-answering` pipeline (which is 384):
-
-```py
-inputs = tokenizer(question, long_context)
-print(len(inputs["input_ids"]))
-```
-
-```python out
-461
-```
-
-So, we'll need to truncate our inputs at that maximum length. There are several ways we can do this, but we don't want to truncate the question, only the context. Since the context is the second sentence, we'll use the `"only_second"` truncation strategy. The problem that arises then is that the answer to the question may not be in the truncated context. Here, for instance, we picked a question where the answer is toward the end of the context, and when we truncate it that answer is not present:
-
-```py
-inputs = tokenizer(question, long_context, max_length=384, truncation="only_second")
-print(tokenizer.decode(inputs["input_ids"]))
-```
-
-```python out
-"""
-[CLS] Which deep learning libraries back [UNK] Transformers? [SEP] [UNK] Transformers : State of the Art NLP
-
-[UNK] Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
-question answering, summarization, translation, text generation and more in over 100 languages.
-Its aim is to make cutting-edge NLP easier to use for everyone.
-
-[UNK] Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
-then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
-can be modified to enable quick research experiments.
-
-Why should I use transformers?
-
-1. Easy-to-use state-of-the-art models:
- - High performance on NLU and NLG tasks.
- - Low barrier to entry for educators and practitioners.
- - Few user-facing abstractions with just three classes to learn.
- - A unified API for using all our pretrained models.
- - Lower compute costs, smaller carbon footprint:
-
-2. Researchers can share trained models instead of always retraining.
- - Practitioners can reduce compute time and production costs.
- - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.
-
-3. Choose the right framework for every part of a model's lifetime:
- - Train state-of-the-art models in 3 lines of code.
- - Move a single model between TF2.0/PyTorch frameworks at will.
- - Seamlessly pick the right framework for training, evaluation and production.
-
-4. Easily customize a model or an example to your needs:
- - We provide examples for each architecture to reproduce the results published by its original authors.
- - Model internal [SEP]
-"""
-```
-
-This means the model will have a hard time picking the correct answer. To fix this, the `question-answering` pipeline allows us to split the context into smaller chunks, specifying the maximum length. To make sure we don't split the context at exactly the wrong place to make it possible to find the answer, it also includes some overlap between the chunks.
-
-We can have the tokenizer (fast or slow) do this for us by adding `return_overflowing_tokens=True`, and we can specify the overlap we want with the `stride` argument. Here is an example, using a smaller sentence:
-
-```py
-sentence = "This sentence is not too long but we are going to split it anyway."
-inputs = tokenizer(
- sentence, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
-)
-
-for ids in inputs["input_ids"]:
- print(tokenizer.decode(ids))
-```
-
-```python out
-'[CLS] This sentence is not [SEP]'
-'[CLS] is not too long [SEP]'
-'[CLS] too long but we [SEP]'
-'[CLS] but we are going [SEP]'
-'[CLS] are going to split [SEP]'
-'[CLS] to split it anyway [SEP]'
-'[CLS] it anyway. [SEP]'
-```
-
-As we can see, the sentence has been split into chunks in such a way that each entry in `inputs["input_ids"]` has at most 6 tokens (we would need to add padding to have the last entry be the same size as the others) and there is an overlap of 2 tokens between each of the entries.
-
-Let's take a closer look at the result of the tokenization:
-
-```py
-print(inputs.keys())
-```
-
-```python out
-dict_keys(['input_ids', 'attention_mask', 'overflow_to_sample_mapping'])
-```
-
-As expected, we get input IDs and an attention mask. The last key, `overflow_to_sample_mapping`, is a map that tells us which sentence each of the results corresponds to -- here we have 7 results that all come from the (only) sentence we passed the tokenizer:
-
-```py
-print(inputs["overflow_to_sample_mapping"])
-```
-
-```python out
-[0, 0, 0, 0, 0, 0, 0]
-```
-
-This is more useful when we tokenize several sentences together. For instance, this:
-
-```py
-sentences = [
- "This sentence is not too long but we are going to split it anyway.",
- "This sentence is shorter but will still get split.",
-]
-inputs = tokenizer(
- sentences, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
-)
-
-print(inputs["overflow_to_sample_mapping"])
-```
-
-gets us:
-
-```python out
-[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]
-```
-
-which means the first sentence is split into 7 chunks as before, and the next 4 chunks come from the second sentence.
-
-Now let's go back to our long context. By default the `question-answering` pipeline uses a maximum length of 384, as we mentioned earlier, and a stride of 128, which correspond to the way the model was fine-tuned (you can adjust those parameters by passing `max_seq_len` and `stride` arguments when calling the pipeline). We will thus use those parameters when tokenizing. We'll also add padding (to have samples of the same length, so we can build tensors) as well as ask for the offsets:
-
-```py
-inputs = tokenizer(
- question,
- long_context,
- stride=128,
- max_length=384,
- padding="longest",
- truncation="only_second",
- return_overflowing_tokens=True,
- return_offsets_mapping=True,
-)
-```
-
-Those `inputs` will contain the input IDs and attention masks the model expects, as well as the offsets and the `overflow_to_sample_mapping` we just talked about. Since those two are not parameters used by the model, we'll pop them out of the `inputs` (and we won't store the map, since it's not useful here) before converting it to a tensor:
-
-{#if fw === 'pt'}
-
-```py
-_ = inputs.pop("overflow_to_sample_mapping")
-offsets = inputs.pop("offset_mapping")
-
-inputs = inputs.convert_to_tensors("pt")
-print(inputs["input_ids"].shape)
-```
-
-```python out
-torch.Size([2, 384])
-```
-
-{:else}
-
-```py
-_ = inputs.pop("overflow_to_sample_mapping")
-offsets = inputs.pop("offset_mapping")
-
-inputs = inputs.convert_to_tensors("tf")
-print(inputs["input_ids"].shape)
-```
-
-```python out
-(2, 384)
-```
-
-{/if}
-
-Our long context was split in two, which means that after it goes through our model, we will have two sets of start and end logits:
-
-```py
-outputs = model(**inputs)
-
-start_logits = outputs.start_logits
-end_logits = outputs.end_logits
-print(start_logits.shape, end_logits.shape)
-```
-
-{#if fw === 'pt'}
-
-```python out
-torch.Size([2, 384]) torch.Size([2, 384])
-```
-
-{:else}
-
-```python out
-(2, 384) (2, 384)
-```
-
-{/if}
-
-Like before, we first mask the tokens that are not part of the context before taking the softmax. We also mask all the padding tokens (as flagged by the attention mask):
-
-{#if fw === 'pt'}
-
-```py
-sequence_ids = inputs.sequence_ids()
-# Mask everything apart from the tokens of the context
-mask = [i != 1 for i in sequence_ids]
-# Unmask the [CLS] token
-mask[0] = False
-# Mask all the [PAD] tokens
-mask = torch.logical_or(torch.tensor(mask)[None], (inputs["attention_mask"] == 0))
-
-start_logits[mask] = -10000
-end_logits[mask] = -10000
-```
-
-{:else}
-
-```py
-sequence_ids = inputs.sequence_ids()
-# Mask everything apart from the tokens of the context
-mask = [i != 1 for i in sequence_ids]
-# Unmask the [CLS] token
-mask[0] = False
-# Mask all the [PAD] tokens
-mask = tf.math.logical_or(tf.constant(mask)[None], inputs["attention_mask"] == 0)
-
-start_logits = tf.where(mask, -10000, start_logits)
-end_logits = tf.where(mask, -10000, end_logits)
-```
-
-{/if}
-
-Then we can use the softmax to convert our logits to probabilities:
-
-{#if fw === 'pt'}
-
-```py
-start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)
-end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)
-```
-
-{:else}
-
-```py
-start_probabilities = tf.math.softmax(start_logits, axis=-1).numpy()
-end_probabilities = tf.math.softmax(end_logits, axis=-1).numpy()
-```
-
-{/if}
-
-The next step is similar to what we did for the small context, but we repeat it for each of our two chunks. We attribute a score to all possible spans of answer, then take the span with the best score:
-
-{#if fw === 'pt'}
-
-```py
-candidates = []
-for start_probs, end_probs in zip(start_probabilities, end_probabilities):
- scores = start_probs[:, None] * end_probs[None, :]
- idx = torch.triu(scores).argmax().item()
-
- start_idx = idx // scores.shape[1]
- end_idx = idx % scores.shape[1]
- score = scores[start_idx, end_idx].item()
- candidates.append((start_idx, end_idx, score))
-
-print(candidates)
-```
-
-{:else}
-
-```py
-candidates = []
-for start_probs, end_probs in zip(start_probabilities, end_probabilities):
- scores = start_probs[:, None] * end_probs[None, :]
- idx = np.triu(scores).argmax().item()
-
- start_idx = idx // scores.shape[1]
- end_idx = idx % scores.shape[1]
- score = scores[start_idx, end_idx].item()
- candidates.append((start_idx, end_idx, score))
-
-print(candidates)
-```
-
-{/if}
-
-```python out
-[(0, 18, 0.33867), (173, 184, 0.97149)]
-```
-
-Those two candidates correspond to the best answers the model was able to find in each chunk. The model is way more confident the right answer is in the second part (which is a good sign!). Now we just have to map those two token spans to spans of characters in the context (we only need to map the second one to have our answer, but it's interesting to see what the model has picked in the first chunk).
-
-> [!TIP]
-> ✏️ **Try it out!** Adapt the code above to return the scores and spans for the five most likely answers (in total, not per chunk).
-
-The `offsets` we grabbed earlier is actually a list of offsets, with one list per chunk of text:
-
-```py
-for candidate, offset in zip(candidates, offsets):
- start_token, end_token, score = candidate
- start_char, _ = offset[start_token]
- _, end_char = offset[end_token]
- answer = long_context[start_char:end_char]
- result = {"answer": answer, "start": start_char, "end": end_char, "score": score}
- print(result)
-```
-
-```python out
-{'answer': '\n🤗 Transformers: State of the Art NLP', 'start': 0, 'end': 37, 'score': 0.33867}
-{'answer': 'Jax, PyTorch and TensorFlow', 'start': 1892, 'end': 1919, 'score': 0.97149}
-```
-
-If we ignore the first result, we get the same result as our pipeline for this long context -- yay!
-
-> [!TIP]
-> ✏️ **Try it out!** Use the best scores you computed before to show the five most likely answers (for the whole context, not each chunk). To check your results, go back to the first pipeline and pass in `top_k=5` when calling it.
-
-This concludes our deep dive into the tokenizer's capabilities. We will put all of this in practice again in the next chapter, when we show you how to fine-tune a model on a range of common NLP tasks.
+
+
+# Fast tokenizers in the QA pipeline[[fast-tokenizers-in-the-qa-pipeline]]
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+We will now dive into the `question-answering` pipeline and see how to leverage the offsets to grab the answer to the question at hand from the context, a bit like we did for the grouped entities in the previous section. Then we will see how we can deal with very long contexts that end up being truncated. You can skip this section if you're not interested in the question answering task.
+
+{#if fw === 'pt'}
+
+
+
+{:else}
+
+
+
+{/if}
+
+## Using the `question-answering` pipeline[[using-the-question-answering-pipeline]]
+
+As we saw in [Chapter 1](/course/chapter1), we can use the `question-answering` pipeline like this to get the answer to a question:
+
+```py
+from transformers import pipeline
+
+question_answerer = pipeline("question-answering")
+context = """
+🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch, and TensorFlow — with a seamless integration
+between them. It's straightforward to train your models with one before loading them for inference with the other.
+"""
+question = "Which deep learning libraries back 🤗 Transformers?"
+question_answerer(question=question, context=context)
+```
+
+```python out
+{'score': 0.97773,
+ 'start': 78,
+ 'end': 105,
+ 'answer': 'Jax, PyTorch and TensorFlow'}
+```
+
+Unlike the other pipelines, which can't truncate and split texts that are longer than the maximum length accepted by the model (and thus may miss information at the end of a document), this pipeline can deal with very long contexts and will return the answer to the question even if it's at the end:
+
+```py
+long_context = """
+🤗 Transformers: State of the Art NLP
+
+🤗 Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
+question answering, summarization, translation, text generation and more in over 100 languages.
+Its aim is to make cutting-edge NLP easier to use for everyone.
+
+🤗 Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
+then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
+can be modified to enable quick research experiments.
+
+Why should I use transformers?
+
+1. Easy-to-use state-of-the-art models:
+ - High performance on NLU and NLG tasks.
+ - Low barrier to entry for educators and practitioners.
+ - Few user-facing abstractions with just three classes to learn.
+ - A unified API for using all our pretrained models.
+ - Lower compute costs, smaller carbon footprint:
+
+2. Researchers can share trained models instead of always retraining.
+ - Practitioners can reduce compute time and production costs.
+ - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.
+
+3. Choose the right framework for every part of a model's lifetime:
+ - Train state-of-the-art models in 3 lines of code.
+ - Move a single model between TF2.0/PyTorch frameworks at will.
+ - Seamlessly pick the right framework for training, evaluation and production.
+
+4. Easily customize a model or an example to your needs:
+ - We provide examples for each architecture to reproduce the results published by its original authors.
+ - Model internals are exposed as consistently as possible.
+ - Model files can be used independently of the library for quick experiments.
+
+🤗 Transformers is backed by the three most popular deep learning libraries — Jax, PyTorch and TensorFlow — with a seamless integration
+between them. It's straightforward to train your models with one before loading them for inference with the other.
+"""
+question_answerer(question=question, context=long_context)
+```
+
+```python out
+{'score': 0.97149,
+ 'start': 1892,
+ 'end': 1919,
+ 'answer': 'Jax, PyTorch and TensorFlow'}
+```
+
+Let's see how it does all of this!
+
+## Using a model for question answering[[using-a-model-for-question-answering]]
+
+Like with any other pipeline, we start by tokenizing our input and then send it through the model. The checkpoint used by default for the `question-answering` pipeline is [`distilbert-base-cased-distilled-squad`](https://huggingface.co/distilbert-base-cased-distilled-squad) (the "squad" in the name comes from the dataset on which the model was fine-tuned; we'll talk more about the SQuAD dataset in [Chapter 7](/course/chapter7/7)):
+
+{#if fw === 'pt'}
+
+```py
+from transformers import AutoTokenizer, AutoModelForQuestionAnswering
+
+model_checkpoint = "distilbert-base-cased-distilled-squad"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = AutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
+
+inputs = tokenizer(question, context, return_tensors="pt")
+outputs = model(**inputs)
+```
+
+{:else}
+
+```py
+from transformers import AutoTokenizer, TFAutoModelForQuestionAnswering
+
+model_checkpoint = "distilbert-base-cased-distilled-squad"
+tokenizer = AutoTokenizer.from_pretrained(model_checkpoint)
+model = TFAutoModelForQuestionAnswering.from_pretrained(model_checkpoint)
+
+inputs = tokenizer(question, context, return_tensors="tf")
+outputs = model(**inputs)
+```
+
+{/if}
+
+Note that we tokenize the question and the context as a pair, with the question first.
+
+
+

+

+
+
+Models for question answering work a little differently from the models we've seen up to now. Using the picture above as an example, the model has been trained to predict the index of the token starting the answer (here 21) and the index of the token where the answer ends (here 24). This is why those models don't return one tensor of logits but two: one for the logits corresponding to the start token of the answer, and one for the logits corresponding to the end token of the answer. Since in this case we have only one input containing 66 tokens, we get:
+
+```py
+start_logits = outputs.start_logits
+end_logits = outputs.end_logits
+print(start_logits.shape, end_logits.shape)
+```
+
+{#if fw === 'pt'}
+
+```python out
+torch.Size([1, 66]) torch.Size([1, 66])
+```
+
+{:else}
+
+```python out
+(1, 66) (1, 66)
+```
+
+{/if}
+
+To convert those logits into probabilities, we will apply a softmax function -- but before that, we need to make sure we mask the indices that are not part of the context. Our input is `[CLS] question [SEP] context [SEP]`, so we need to mask the tokens of the question as well as the `[SEP]` token. We'll keep the `[CLS]` token, however, as some models use it to indicate that the answer is not in the context.
+
+Since we will apply a softmax afterward, we just need to replace the logits we want to mask with a large negative number. Here, we use `-10000`:
+
+{#if fw === 'pt'}
+
+```py
+import torch
+
+sequence_ids = inputs.sequence_ids()
+# Mask everything apart from the tokens of the context
+mask = [i != 1 for i in sequence_ids]
+# Unmask the [CLS] token
+mask[0] = False
+mask = torch.tensor(mask)[None]
+
+start_logits[mask] = -10000
+end_logits[mask] = -10000
+```
+
+{:else}
+
+```py
+import tensorflow as tf
+
+sequence_ids = inputs.sequence_ids()
+# Mask everything apart from the tokens of the context
+mask = [i != 1 for i in sequence_ids]
+# Unmask the [CLS] token
+mask[0] = False
+mask = tf.constant(mask)[None]
+
+start_logits = tf.where(mask, -10000, start_logits)
+end_logits = tf.where(mask, -10000, end_logits)
+```
+
+{/if}
+
+Now that we have properly masked the logits corresponding to positions we don't want to predict, we can apply the softmax:
+
+{#if fw === 'pt'}
+
+```py
+start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)[0]
+end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)[0]
+```
+
+{:else}
+
+```py
+start_probabilities = tf.math.softmax(start_logits, axis=-1)[0].numpy()
+end_probabilities = tf.math.softmax(end_logits, axis=-1)[0].numpy()
+```
+
+{/if}
+
+At this stage, we could take the argmax of the start and end probabilities -- but we might end up with a start index that is greater than the end index, so we need to take a few more precautions. We will compute the probabilities of each possible `start_index` and `end_index` where `start_index <= end_index`, then take the tuple `(start_index, end_index)` with the highest probability.
+
+Assuming the events "The answer starts at `start_index`" and "The answer ends at `end_index`" to be independent, the probability that the answer starts at `start_index` and ends at `end_index` is:
+
+$$\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]$$
+
+So, to compute all the scores, we just need to compute all the products \\(\mathrm{start\_probabilities}[\mathrm{start\_index}] \times \mathrm{end\_probabilities}[\mathrm{end\_index}]\\) where `start_index <= end_index`.
+
+First let's compute all the possible products:
+
+```py
+scores = start_probabilities[:, None] * end_probabilities[None, :]
+```
+
+{#if fw === 'pt'}
+
+Then we'll mask the values where `start_index > end_index` by setting them to `0` (the other probabilities are all positive numbers). The `torch.triu()` function returns the upper triangular part of the 2D tensor passed as an argument, so it will do that masking for us:
+
+```py
+scores = torch.triu(scores)
+```
+
+{:else}
+
+Then we'll mask the values where `start_index > end_index` by setting them to `0` (the other probabilities are all positive numbers). The `np.triu()` function returns the upper triangular part of the 2D tensor passed as an argument, so it will do that masking for us:
+
+```py
+import numpy as np
+
+scores = np.triu(scores)
+```
+
+{/if}
+
+Now we just have to get the index of the maximum. Since PyTorch will return the index in the flattened tensor, we need to use the floor division `//` and modulus `%` operations to get the `start_index` and `end_index`:
+
+```py
+max_index = scores.argmax().item()
+start_index = max_index // scores.shape[1]
+end_index = max_index % scores.shape[1]
+print(scores[start_index, end_index])
+```
+
+We're not quite done yet, but at least we already have the correct score for the answer (you can check this by comparing it to the first result in the previous section):
+
+```python out
+0.97773
+```
+
+> [!TIP]
+> ✏️ **Try it out!** Compute the start and end indices for the five most likely answers.
+
+We have the `start_index` and `end_index` of the answer in terms of tokens, so now we just need to convert to the character indices in the context. This is where the offsets will be super useful. We can grab them and use them like we did in the token classification task:
+
+```py
+inputs_with_offsets = tokenizer(question, context, return_offsets_mapping=True)
+offsets = inputs_with_offsets["offset_mapping"]
+
+start_char, _ = offsets[start_index]
+_, end_char = offsets[end_index]
+answer = context[start_char:end_char]
+```
+
+Now we just have to format everything to get our result:
+
+```py
+result = {
+ "answer": answer,
+ "start": start_char,
+ "end": end_char,
+ "score": scores[start_index, end_index],
+}
+print(result)
+```
+
+```python out
+{'answer': 'Jax, PyTorch and TensorFlow',
+ 'start': 78,
+ 'end': 105,
+ 'score': 0.97773}
+```
+
+Great! That's the same as in our first example!
+
+> [!TIP]
+> ✏️ **Try it out!** Use the best scores you computed earlier to show the five most likely answers. To check your results, go back to the first pipeline and pass in `top_k=5` when calling it.
+
+## Handling long contexts[[handling-long-contexts]]
+
+If we try to tokenize the question and long context we used as an example previously, we'll get a number of tokens higher than the maximum length used in the `question-answering` pipeline (which is 384):
+
+```py
+inputs = tokenizer(question, long_context)
+print(len(inputs["input_ids"]))
+```
+
+```python out
+461
+```
+
+So, we'll need to truncate our inputs at that maximum length. There are several ways we can do this, but we don't want to truncate the question, only the context. Since the context is the second sentence, we'll use the `"only_second"` truncation strategy. The problem that arises then is that the answer to the question may not be in the truncated context. Here, for instance, we picked a question where the answer is toward the end of the context, and when we truncate it that answer is not present:
+
+```py
+inputs = tokenizer(question, long_context, max_length=384, truncation="only_second")
+print(tokenizer.decode(inputs["input_ids"]))
+```
+
+```python out
+"""
+[CLS] Which deep learning libraries back [UNK] Transformers? [SEP] [UNK] Transformers : State of the Art NLP
+
+[UNK] Transformers provides thousands of pretrained models to perform tasks on texts such as classification, information extraction,
+question answering, summarization, translation, text generation and more in over 100 languages.
+Its aim is to make cutting-edge NLP easier to use for everyone.
+
+[UNK] Transformers provides APIs to quickly download and use those pretrained models on a given text, fine-tune them on your own datasets and
+then share them with the community on our model hub. At the same time, each python module defining an architecture is fully standalone and
+can be modified to enable quick research experiments.
+
+Why should I use transformers?
+
+1. Easy-to-use state-of-the-art models:
+ - High performance on NLU and NLG tasks.
+ - Low barrier to entry for educators and practitioners.
+ - Few user-facing abstractions with just three classes to learn.
+ - A unified API for using all our pretrained models.
+ - Lower compute costs, smaller carbon footprint:
+
+2. Researchers can share trained models instead of always retraining.
+ - Practitioners can reduce compute time and production costs.
+ - Dozens of architectures with over 10,000 pretrained models, some in more than 100 languages.
+
+3. Choose the right framework for every part of a model's lifetime:
+ - Train state-of-the-art models in 3 lines of code.
+ - Move a single model between TF2.0/PyTorch frameworks at will.
+ - Seamlessly pick the right framework for training, evaluation and production.
+
+4. Easily customize a model or an example to your needs:
+ - We provide examples for each architecture to reproduce the results published by its original authors.
+ - Model internal [SEP]
+"""
+```
+
+This means the model will have a hard time picking the correct answer. To fix this, the `question-answering` pipeline allows us to split the context into smaller chunks, specifying the maximum length. To make sure we don't split the context at exactly the wrong place to make it possible to find the answer, it also includes some overlap between the chunks.
+
+We can have the tokenizer (fast or slow) do this for us by adding `return_overflowing_tokens=True`, and we can specify the overlap we want with the `stride` argument. Here is an example, using a smaller sentence:
+
+```py
+sentence = "This sentence is not too long but we are going to split it anyway."
+inputs = tokenizer(
+ sentence, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
+)
+
+for ids in inputs["input_ids"]:
+ print(tokenizer.decode(ids))
+```
+
+```python out
+'[CLS] This sentence is not [SEP]'
+'[CLS] is not too long [SEP]'
+'[CLS] too long but we [SEP]'
+'[CLS] but we are going [SEP]'
+'[CLS] are going to split [SEP]'
+'[CLS] to split it anyway [SEP]'
+'[CLS] it anyway. [SEP]'
+```
+
+As we can see, the sentence has been split into chunks in such a way that each entry in `inputs["input_ids"]` has at most 6 tokens (we would need to add padding to have the last entry be the same size as the others) and there is an overlap of 2 tokens between each of the entries.
+
+Let's take a closer look at the result of the tokenization:
+
+```py
+print(inputs.keys())
+```
+
+```python out
+dict_keys(['input_ids', 'attention_mask', 'overflow_to_sample_mapping'])
+```
+
+As expected, we get input IDs and an attention mask. The last key, `overflow_to_sample_mapping`, is a map that tells us which sentence each of the results corresponds to -- here we have 7 results that all come from the (only) sentence we passed the tokenizer:
+
+```py
+print(inputs["overflow_to_sample_mapping"])
+```
+
+```python out
+[0, 0, 0, 0, 0, 0, 0]
+```
+
+This is more useful when we tokenize several sentences together. For instance, this:
+
+```py
+sentences = [
+ "This sentence is not too long but we are going to split it anyway.",
+ "This sentence is shorter but will still get split.",
+]
+inputs = tokenizer(
+ sentences, truncation=True, return_overflowing_tokens=True, max_length=6, stride=2
+)
+
+print(inputs["overflow_to_sample_mapping"])
+```
+
+gets us:
+
+```python out
+[0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1]
+```
+
+which means the first sentence is split into 7 chunks as before, and the next 4 chunks come from the second sentence.
+
+Now let's go back to our long context. By default the `question-answering` pipeline uses a maximum length of 384, as we mentioned earlier, and a stride of 128, which correspond to the way the model was fine-tuned (you can adjust those parameters by passing `max_seq_len` and `stride` arguments when calling the pipeline). We will thus use those parameters when tokenizing. We'll also add padding (to have samples of the same length, so we can build tensors) as well as ask for the offsets:
+
+```py
+inputs = tokenizer(
+ question,
+ long_context,
+ stride=128,
+ max_length=384,
+ padding="longest",
+ truncation="only_second",
+ return_overflowing_tokens=True,
+ return_offsets_mapping=True,
+)
+```
+
+Those `inputs` will contain the input IDs and attention masks the model expects, as well as the offsets and the `overflow_to_sample_mapping` we just talked about. Since those two are not parameters used by the model, we'll pop them out of the `inputs` (and we won't store the map, since it's not useful here) before converting it to a tensor:
+
+{#if fw === 'pt'}
+
+```py
+_ = inputs.pop("overflow_to_sample_mapping")
+offsets = inputs.pop("offset_mapping")
+
+inputs = inputs.convert_to_tensors("pt")
+print(inputs["input_ids"].shape)
+```
+
+```python out
+torch.Size([2, 384])
+```
+
+{:else}
+
+```py
+_ = inputs.pop("overflow_to_sample_mapping")
+offsets = inputs.pop("offset_mapping")
+
+inputs = inputs.convert_to_tensors("tf")
+print(inputs["input_ids"].shape)
+```
+
+```python out
+(2, 384)
+```
+
+{/if}
+
+Our long context was split in two, which means that after it goes through our model, we will have two sets of start and end logits:
+
+```py
+outputs = model(**inputs)
+
+start_logits = outputs.start_logits
+end_logits = outputs.end_logits
+print(start_logits.shape, end_logits.shape)
+```
+
+{#if fw === 'pt'}
+
+```python out
+torch.Size([2, 384]) torch.Size([2, 384])
+```
+
+{:else}
+
+```python out
+(2, 384) (2, 384)
+```
+
+{/if}
+
+Like before, we first mask the tokens that are not part of the context before taking the softmax. We also mask all the padding tokens (as flagged by the attention mask):
+
+{#if fw === 'pt'}
+
+```py
+sequence_ids = inputs.sequence_ids()
+# Mask everything apart from the tokens of the context
+mask = [i != 1 for i in sequence_ids]
+# Unmask the [CLS] token
+mask[0] = False
+# Mask all the [PAD] tokens
+mask = torch.logical_or(torch.tensor(mask)[None], (inputs["attention_mask"] == 0))
+
+start_logits[mask] = -10000
+end_logits[mask] = -10000
+```
+
+{:else}
+
+```py
+sequence_ids = inputs.sequence_ids()
+# Mask everything apart from the tokens of the context
+mask = [i != 1 for i in sequence_ids]
+# Unmask the [CLS] token
+mask[0] = False
+# Mask all the [PAD] tokens
+mask = tf.math.logical_or(tf.constant(mask)[None], inputs["attention_mask"] == 0)
+
+start_logits = tf.where(mask, -10000, start_logits)
+end_logits = tf.where(mask, -10000, end_logits)
+```
+
+{/if}
+
+Then we can use the softmax to convert our logits to probabilities:
+
+{#if fw === 'pt'}
+
+```py
+start_probabilities = torch.nn.functional.softmax(start_logits, dim=-1)
+end_probabilities = torch.nn.functional.softmax(end_logits, dim=-1)
+```
+
+{:else}
+
+```py
+start_probabilities = tf.math.softmax(start_logits, axis=-1).numpy()
+end_probabilities = tf.math.softmax(end_logits, axis=-1).numpy()
+```
+
+{/if}
+
+The next step is similar to what we did for the small context, but we repeat it for each of our two chunks. We attribute a score to all possible spans of answer, then take the span with the best score:
+
+{#if fw === 'pt'}
+
+```py
+candidates = []
+for start_probs, end_probs in zip(start_probabilities, end_probabilities):
+ scores = start_probs[:, None] * end_probs[None, :]
+ idx = torch.triu(scores).argmax().item()
+
+ start_idx = idx // scores.shape[1]
+ end_idx = idx % scores.shape[1]
+ score = scores[start_idx, end_idx].item()
+ candidates.append((start_idx, end_idx, score))
+
+print(candidates)
+```
+
+{:else}
+
+```py
+candidates = []
+for start_probs, end_probs in zip(start_probabilities, end_probabilities):
+ scores = start_probs[:, None] * end_probs[None, :]
+ idx = np.triu(scores).argmax().item()
+
+ start_idx = idx // scores.shape[1]
+ end_idx = idx % scores.shape[1]
+ score = scores[start_idx, end_idx].item()
+ candidates.append((start_idx, end_idx, score))
+
+print(candidates)
+```
+
+{/if}
+
+```python out
+[(0, 18, 0.33867), (173, 184, 0.97149)]
+```
+
+Those two candidates correspond to the best answers the model was able to find in each chunk. The model is way more confident the right answer is in the second part (which is a good sign!). Now we just have to map those two token spans to spans of characters in the context (we only need to map the second one to have our answer, but it's interesting to see what the model has picked in the first chunk).
+
+> [!TIP]
+> ✏️ **Try it out!** Adapt the code above to return the scores and spans for the five most likely answers (in total, not per chunk).
+
+The `offsets` we grabbed earlier is actually a list of offsets, with one list per chunk of text:
+
+```py
+for candidate, offset in zip(candidates, offsets):
+ start_token, end_token, score = candidate
+ start_char, _ = offset[start_token]
+ _, end_char = offset[end_token]
+ answer = long_context[start_char:end_char]
+ result = {"answer": answer, "start": start_char, "end": end_char, "score": score}
+ print(result)
+```
+
+```python out
+{'answer': '\n🤗 Transformers: State of the Art NLP', 'start': 0, 'end': 37, 'score': 0.33867}
+{'answer': 'Jax, PyTorch and TensorFlow', 'start': 1892, 'end': 1919, 'score': 0.97149}
+```
+
+If we ignore the first result, we get the same result as our pipeline for this long context -- yay!
+
+> [!TIP]
+> ✏️ **Try it out!** Use the best scores you computed before to show the five most likely answers (for the whole context, not each chunk). To check your results, go back to the first pipeline and pass in `top_k=5` when calling it.
+
+This concludes our deep dive into the tokenizer's capabilities. We will put all of this in practice again in the next chapter, when we show you how to fine-tune a model on a range of common NLP tasks.
diff --git a/chapters/en/chapter6/4.mdx b/chapters/en/chapter6/4.mdx
index 5008699c6..1d0a20866 100644
--- a/chapters/en/chapter6/4.mdx
+++ b/chapters/en/chapter6/4.mdx
@@ -1,120 +1,120 @@
-# Normalization and pre-tokenization[[normalization-and-pre-tokenization]]
-
-
-
-Before we dive more deeply into the three most common subword tokenization algorithms used with Transformer models (Byte-Pair Encoding [BPE], WordPiece, and Unigram), we'll first take a look at the preprocessing that each tokenizer applies to text. Here's a high-level overview of the steps in the tokenization pipeline:
-
-
-

-

-
-
-Before splitting a text into subtokens (according to its model), the tokenizer performs two steps: _normalization_ and _pre-tokenization_.
-
-## Normalization[[normalization]]
-
-
-
-The normalization step involves some general cleanup, such as removing needless whitespace, lowercasing, and/or removing accents. If you're familiar with [Unicode normalization](http://www.unicode.org/reports/tr15/) (such as NFC or NFKC), this is also something the tokenizer may apply.
-
-The 🤗 Transformers `tokenizer` has an attribute called `backend_tokenizer` that provides access to the underlying tokenizer from the 🤗 Tokenizers library:
-
-```py
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
-print(type(tokenizer.backend_tokenizer))
-```
-
-```python out
-
-```
-
-The `normalizer` attribute of the `tokenizer` object has a `normalize_str()` method that we can use to see how the normalization is performed:
-
-```py
-print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
-```
-
-```python out
-'hello how are u?'
-```
-
-In this example, since we picked the `bert-base-uncased` checkpoint, the normalization applied lowercasing and removed the accents.
-
-> [!TIP]
-> ✏️ **Try it out!** Load a tokenizer from the `bert-base-cased` checkpoint and pass the same example to it. What are the main differences you can see between the cased and uncased versions of the tokenizer?
-
-## Pre-tokenization[[pre-tokenization]]
-
-
-
-As we will see in the next sections, a tokenizer cannot be trained on raw text alone. Instead, we first need to split the texts into small entities, like words. That's where the pre-tokenization step comes in. As we saw in [Chapter 2](/course/chapter2), a word-based tokenizer can simply split a raw text into words on whitespace and punctuation. Those words will be the boundaries of the subtokens the tokenizer can learn during its training.
-
-To see how a fast tokenizer performs pre-tokenization, we can use the `pre_tokenize_str()` method of the `pre_tokenizer` attribute of the `tokenizer` object:
-
-```py
-tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
-```
-
-```python out
-[('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))]
-```
-
-Notice how the tokenizer is already keeping track of the offsets, which is how it can give us the offset mapping we used in the previous section. Here the tokenizer ignores the two spaces and replaces them with just one, but the offset jumps between `are` and `you` to account for that.
-
-Since we're using a BERT tokenizer, the pre-tokenization involves splitting on whitespace and punctuation. Other tokenizers can have different rules for this step. For example, if we use the GPT-2 tokenizer:
-
-```py
-tokenizer = AutoTokenizer.from_pretrained("gpt2")
-tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
-```
-
-it will split on whitespace and punctuation as well, but it will keep the spaces and replace them with a `Ġ` symbol, enabling it to recover the original spaces if we decode the tokens:
-
-```python out
-[('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)),
- ('?', (19, 20))]
-```
-
-Also note that unlike the BERT tokenizer, this tokenizer does not ignore the double space.
-
-For a last example, let's have a look at the T5 tokenizer, which is based on the SentencePiece algorithm:
-
-```py
-tokenizer = AutoTokenizer.from_pretrained("t5-small")
-tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
-```
-
-```python out
-[('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))]
-```
-
-Like the GPT-2 tokenizer, this one keeps spaces and replaces them with a specific token (`_`), but the T5 tokenizer only splits on whitespace, not punctuation. Also note that it added a space by default at the beginning of the sentence (before `Hello`) and ignored the double space between `are` and `you`.
-
-Now that we've seen a little of how some different tokenizers process text, we can start to explore the underlying algorithms themselves. We'll begin with a quick look at the broadly widely applicable SentencePiece; then, over the next three sections, we'll examine how the three main algorithms used for subword tokenization work.
-
-## SentencePiece[[sentencepiece]]
-
-[SentencePiece](https://github.com/google/sentencepiece) is a tokenization algorithm for the preprocessing of text that you can use with any of the models we will see in the next three sections. It considers the text as a sequence of Unicode characters, and replaces spaces with a special character, `▁`. Used in conjunction with the Unigram algorithm (see [section 7](/course/chapter6/7)), it doesn't even require a pre-tokenization step, which is very useful for languages where the space character is not used (like Chinese or Japanese).
-
-The other main feature of SentencePiece is *reversible tokenization*: since there is no special treatment of spaces, decoding the tokens is done simply by concatenating them and replacing the `_`s with spaces -- this results in the normalized text. As we saw earlier, the BERT tokenizer removes repeating spaces, so its tokenization is not reversible.
-
-## Algorithm overview[[algorithm-overview]]
-
-In the following sections, we'll dive into the three main subword tokenization algorithms: BPE (used by GPT-2 and others), WordPiece (used for example by BERT), and Unigram (used by T5 and others). Before we get started, here's a quick overview of how they each work. Don't hesitate to come back to this table after reading each of the next sections if it doesn't make sense to you yet.
-
-
-Model | BPE | WordPiece | Unigram
-:----:|:---:|:---------:|:------:
-Training | Starts from a small vocabulary and learns rules to merge tokens | Starts from a small vocabulary and learns rules to merge tokens | Starts from a large vocabulary and learns rules to remove tokens
-Training step | Merges the tokens corresponding to the most common pair | Merges the tokens corresponding to the pair with the best score based on the frequency of the pair, privileging pairs where each individual token is less frequent | Removes all the tokens in the vocabulary that will minimize the loss computed on the whole corpus
-Learns | Merge rules and a vocabulary | Just a vocabulary | A vocabulary with a score for each token
-Encoding | Splits a word into characters and applies the merges learned during training | Finds the longest subword starting from the beginning that is in the vocabulary, then does the same for the rest of the word | Finds the most likely split into tokens, using the scores learned during training
-
-Now let's dive into BPE!
+# Normalization and pre-tokenization[[normalization-and-pre-tokenization]]
+
+
+
+Before we dive more deeply into the three most common subword tokenization algorithms used with Transformer models (Byte-Pair Encoding [BPE], WordPiece, and Unigram), we'll first take a look at the preprocessing that each tokenizer applies to text. Here's a high-level overview of the steps in the tokenization pipeline:
+
+
+

+

+
+
+Before splitting a text into subtokens (according to its model), the tokenizer performs two steps: _normalization_ and _pre-tokenization_.
+
+## Normalization[[normalization]]
+
+
+
+The normalization step involves some general cleanup, such as removing needless whitespace, lowercasing, and/or removing accents. If you're familiar with [Unicode normalization](http://www.unicode.org/reports/tr15/) (such as NFC or NFKC), this is also something the tokenizer may apply.
+
+The 🤗 Transformers `tokenizer` has an attribute called `backend_tokenizer` that provides access to the underlying tokenizer from the 🤗 Tokenizers library:
+
+```py
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-uncased")
+print(type(tokenizer.backend_tokenizer))
+```
+
+```python out
+
+```
+
+The `normalizer` attribute of the `tokenizer` object has a `normalize_str()` method that we can use to see how the normalization is performed:
+
+```py
+print(tokenizer.backend_tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
+```
+
+```python out
+'hello how are u?'
+```
+
+In this example, since we picked the `bert-base-uncased` checkpoint, the normalization applied lowercasing and removed the accents.
+
+> [!TIP]
+> ✏️ **Try it out!** Load a tokenizer from the `bert-base-cased` checkpoint and pass the same example to it. What are the main differences you can see between the cased and uncased versions of the tokenizer?
+
+## Pre-tokenization[[pre-tokenization]]
+
+
+
+As we will see in the next sections, a tokenizer cannot be trained on raw text alone. Instead, we first need to split the texts into small entities, like words. That's where the pre-tokenization step comes in. As we saw in [Chapter 2](/course/chapter2), a word-based tokenizer can simply split a raw text into words on whitespace and punctuation. Those words will be the boundaries of the subtokens the tokenizer can learn during its training.
+
+To see how a fast tokenizer performs pre-tokenization, we can use the `pre_tokenize_str()` method of the `pre_tokenizer` attribute of the `tokenizer` object:
+
+```py
+tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
+```
+
+```python out
+[('Hello', (0, 5)), (',', (5, 6)), ('how', (7, 10)), ('are', (11, 14)), ('you', (16, 19)), ('?', (19, 20))]
+```
+
+Notice how the tokenizer is already keeping track of the offsets, which is how it can give us the offset mapping we used in the previous section. Here the tokenizer ignores the two spaces and replaces them with just one, but the offset jumps between `are` and `you` to account for that.
+
+Since we're using a BERT tokenizer, the pre-tokenization involves splitting on whitespace and punctuation. Other tokenizers can have different rules for this step. For example, if we use the GPT-2 tokenizer:
+
+```py
+tokenizer = AutoTokenizer.from_pretrained("gpt2")
+tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
+```
+
+it will split on whitespace and punctuation as well, but it will keep the spaces and replace them with a `Ġ` symbol, enabling it to recover the original spaces if we decode the tokens:
+
+```python out
+[('Hello', (0, 5)), (',', (5, 6)), ('Ġhow', (6, 10)), ('Ġare', (10, 14)), ('Ġ', (14, 15)), ('Ġyou', (15, 19)),
+ ('?', (19, 20))]
+```
+
+Also note that unlike the BERT tokenizer, this tokenizer does not ignore the double space.
+
+For a last example, let's have a look at the T5 tokenizer, which is based on the SentencePiece algorithm:
+
+```py
+tokenizer = AutoTokenizer.from_pretrained("t5-small")
+tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str("Hello, how are you?")
+```
+
+```python out
+[('▁Hello,', (0, 6)), ('▁how', (7, 10)), ('▁are', (11, 14)), ('▁you?', (16, 20))]
+```
+
+Like the GPT-2 tokenizer, this one keeps spaces and replaces them with a specific token (`_`), but the T5 tokenizer only splits on whitespace, not punctuation. Also note that it added a space by default at the beginning of the sentence (before `Hello`) and ignored the double space between `are` and `you`.
+
+Now that we've seen a little of how some different tokenizers process text, we can start to explore the underlying algorithms themselves. We'll begin with a quick look at the broadly widely applicable SentencePiece; then, over the next three sections, we'll examine how the three main algorithms used for subword tokenization work.
+
+## SentencePiece[[sentencepiece]]
+
+[SentencePiece](https://github.com/google/sentencepiece) is a tokenization algorithm for the preprocessing of text that you can use with any of the models we will see in the next three sections. It considers the text as a sequence of Unicode characters, and replaces spaces with a special character, `▁`. Used in conjunction with the Unigram algorithm (see [section 7](/course/chapter6/7)), it doesn't even require a pre-tokenization step, which is very useful for languages where the space character is not used (like Chinese or Japanese).
+
+The other main feature of SentencePiece is *reversible tokenization*: since there is no special treatment of spaces, decoding the tokens is done simply by concatenating them and replacing the `_`s with spaces -- this results in the normalized text. As we saw earlier, the BERT tokenizer removes repeating spaces, so its tokenization is not reversible.
+
+## Algorithm overview[[algorithm-overview]]
+
+In the following sections, we'll dive into the three main subword tokenization algorithms: BPE (used by GPT-2 and others), WordPiece (used for example by BERT), and Unigram (used by T5 and others). Before we get started, here's a quick overview of how they each work. Don't hesitate to come back to this table after reading each of the next sections if it doesn't make sense to you yet.
+
+
+Model | BPE | WordPiece | Unigram
+:----:|:---:|:---------:|:------:
+Training | Starts from a small vocabulary and learns rules to merge tokens | Starts from a small vocabulary and learns rules to merge tokens | Starts from a large vocabulary and learns rules to remove tokens
+Training step | Merges the tokens corresponding to the most common pair | Merges the tokens corresponding to the pair with the best score based on the frequency of the pair, privileging pairs where each individual token is less frequent | Removes all the tokens in the vocabulary that will minimize the loss computed on the whole corpus
+Learns | Merge rules and a vocabulary | Just a vocabulary | A vocabulary with a score for each token
+Encoding | Splits a word into characters and applies the merges learned during training | Finds the longest subword starting from the beginning that is in the vocabulary, then does the same for the rest of the word | Finds the most likely split into tokens, using the scores learned during training
+
+Now let's dive into BPE!
diff --git a/chapters/en/chapter6/5.mdx b/chapters/en/chapter6/5.mdx
index e8375d2c9..c608d440d 100644
--- a/chapters/en/chapter6/5.mdx
+++ b/chapters/en/chapter6/5.mdx
@@ -1,342 +1,342 @@
-# Byte-Pair Encoding tokenization[[byte-pair-encoding-tokenization]]
-
-
-
-Byte-Pair Encoding (BPE) was initially developed as an algorithm to compress texts, and then used by OpenAI for tokenization when pretraining the GPT model. It's used by a lot of Transformer models, including GPT, GPT-2, RoBERTa, BART, and DeBERTa.
-
-
-
-> [!TIP]
-> 💡 This section covers BPE in depth, going as far as showing a full implementation. You can skip to the end if you just want a general overview of the tokenization algorithm.
-
-## Training algorithm[[training-algorithm]]
-
-BPE training starts by computing the unique set of words used in the corpus (after the normalization and pre-tokenization steps are completed), then building the vocabulary by taking all the symbols used to write those words. As a very simple example, let's say our corpus uses these five words:
-
-```
-"hug", "pug", "pun", "bun", "hugs"
-```
-
-The base vocabulary will then be `["b", "g", "h", "n", "p", "s", "u"]`. For real-world cases, that base vocabulary will contain all the ASCII characters, at the very least, and probably some Unicode characters as well. If an example you are tokenizing uses a character that is not in the training corpus, that character will be converted to the unknown token. That's one reason why lots of NLP models are very bad at analyzing content with emojis, for instance.
-
-> [!TIP]
-> The GPT-2 and RoBERTa tokenizers (which are pretty similar) have a clever way to deal with this: they don't look at words as being written with Unicode characters, but with bytes. This way the base vocabulary has a small size (256), but every character you can think of will still be included and not end up being converted to the unknown token. This trick is called *byte-level BPE*.
-
-After getting this base vocabulary, we add new tokens until the desired vocabulary size is reached by learning *merges*, which are rules to merge two elements of the existing vocabulary together into a new one. So, at the beginning these merges will create tokens with two characters, and then, as training progresses, longer subwords.
-
-At any step during the tokenizer training, the BPE algorithm will search for the most frequent pair of existing tokens (by "pair," here we mean two consecutive tokens in a word). That most frequent pair is the one that will be merged, and we rinse and repeat for the next step.
-
-Going back to our previous example, let's assume the words had the following frequencies:
-
-```
-("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
-```
-
-meaning `"hug"` was present 10 times in the corpus, `"pug"` 5 times, `"pun"` 12 times, `"bun"` 4 times, and `"hugs"` 5 times. We start the training by splitting each word into characters (the ones that form our initial vocabulary) so we can see each word as a list of tokens:
-
-```
-("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)
-```
-
-Then we look at pairs. The pair `("h", "u")` is present in the words `"hug"` and `"hugs"`, so 15 times total in the corpus. It's not the most frequent pair, though: that honor belongs to `("u", "g")`, which is present in `"hug"`, `"pug"`, and `"hugs"`, for a grand total of 20 times in the vocabulary.
-
-Thus, the first merge rule learned by the tokenizer is `("u", "g") -> "ug"`, which means that `"ug"` will be added to the vocabulary, and the pair should be merged in all the words of the corpus. At the end of this stage, the vocabulary and corpus look like this:
-
-```
-Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug"]
-Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)
-```
-
-Now we have some pairs that result in a token longer than two characters: the pair `("h", "ug")`, for instance (present 15 times in the corpus). The most frequent pair at this stage is `("u", "n")`, however, present 16 times in the corpus, so the second merge rule learned is `("u", "n") -> "un"`. Adding that to the vocabulary and merging all existing occurrences leads us to:
-
-```
-Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un"]
-Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("h" "ug" "s", 5)
-```
-
-Now the most frequent pair is `("h", "ug")`, so we learn the merge rule `("h", "ug") -> "hug"`, which gives us our first three-letter token. After the merge, the corpus looks like this:
-
-```
-Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]
-Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)
-```
-
-And we continue like this until we reach the desired vocabulary size.
-
-> [!TIP]
-> ✏️ **Now your turn!** What do you think the next merge rule will be?
-
-## Tokenization algorithm[[tokenization-algorithm]]
-
-Tokenization follows the training process closely, in the sense that new inputs are tokenized by applying the following steps:
-
-1. Normalization
-2. Pre-tokenization
-3. Splitting the words into individual characters
-4. Applying the merge rules learned in order on those splits
-
-Let's take the example we used during training, with the three merge rules learned:
-
-```
-("u", "g") -> "ug"
-("u", "n") -> "un"
-("h", "ug") -> "hug"
-```
-
-The word `"bug"` will be tokenized as `["b", "ug"]`. `"mug"`, however, will be tokenized as `["[UNK]", "ug"]` since the letter `"m"` was not in the base vocabulary. Likewise, the word `"thug"` will be tokenized as `["[UNK]", "hug"]`: the letter `"t"` is not in the base vocabulary, and applying the merge rules results first in `"u"` and `"g"` being merged and then `"h"` and `"ug"` being merged.
-
-> [!TIP]
-> ✏️ **Now your turn!** How do you think the word `"unhug"` will be tokenized?
-
-## Implementing BPE[[implementing-bpe]]
-
-Now let's take a look at an implementation of the BPE algorithm. This won't be an optimized version you can actually use on a big corpus; we just want to show you the code so you can understand the algorithm a little bit better.
-
-First we need a corpus, so let's create a simple one with a few sentences:
-
-```python
-corpus = [
- "This is the Hugging Face Course.",
- "This chapter is about tokenization.",
- "This section shows several tokenizer algorithms.",
- "Hopefully, you will be able to understand how they are trained and generate tokens.",
-]
-```
-
-Next, we need to pre-tokenize that corpus into words. Since we are replicating a BPE tokenizer (like GPT-2), we will use the `gpt2` tokenizer for the pre-tokenization:
-
-```python
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("gpt2")
-```
-
-Then we compute the frequencies of each word in the corpus as we do the pre-tokenization:
-
-```python
-from collections import defaultdict
-
-word_freqs = defaultdict(int)
-
-for text in corpus:
- words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
- new_words = [word for word, offset in words_with_offsets]
- for word in new_words:
- word_freqs[word] += 1
-
-print(word_freqs)
-```
-
-```python out
-defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1,
- 'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1,
- 'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1,
- 'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1})
-```
-
-The next step is to compute the base vocabulary, formed by all the characters used in the corpus:
-
-```python
-alphabet = []
-
-for word in word_freqs.keys():
- for letter in word:
- if letter not in alphabet:
- alphabet.append(letter)
-alphabet.sort()
-
-print(alphabet)
-```
-
-```python out
-[ ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's',
- 't', 'u', 'v', 'w', 'y', 'z', 'Ġ']
-```
-
-We also add the special tokens used by the model at the beginning of that vocabulary. In the case of GPT-2, the only special token is `"<|endoftext|>"`:
-
-```python
-vocab = ["<|endoftext|>"] + alphabet.copy()
-```
-
-We now need to split each word into individual characters, to be able to start training:
-
-```python
-splits = {word: [c for c in word] for word in word_freqs.keys()}
-```
-
-Now that we are ready for training, let's write a function that computes the frequency of each pair. We'll need to use this at each step of the training:
-
-```python
-def compute_pair_freqs(splits):
- pair_freqs = defaultdict(int)
- for word, freq in word_freqs.items():
- split = splits[word]
- if len(split) == 1:
- continue
- for i in range(len(split) - 1):
- pair = (split[i], split[i + 1])
- pair_freqs[pair] += freq
- return pair_freqs
-```
-
-Let's have a look at a part of this dictionary after the initial splits:
-
-```python
-pair_freqs = compute_pair_freqs(splits)
-
-for i, key in enumerate(pair_freqs.keys()):
- print(f"{key}: {pair_freqs[key]}")
- if i >= 5:
- break
-```
-
-```python out
-('T', 'h'): 3
-('h', 'i'): 3
-('i', 's'): 5
-('Ġ', 'i'): 2
-('Ġ', 't'): 7
-('t', 'h'): 3
-```
-
-Now, finding the most frequent pair only takes a quick loop:
-
-```python
-best_pair = ""
-max_freq = None
-
-for pair, freq in pair_freqs.items():
- if max_freq is None or max_freq < freq:
- best_pair = pair
- max_freq = freq
-
-print(best_pair, max_freq)
-```
-
-```python out
-('Ġ', 't') 7
-```
-
-So the first merge to learn is `('Ġ', 't') -> 'Ġt'`, and we add `'Ġt'` to the vocabulary:
-
-```python
-merges = {("Ġ", "t"): "Ġt"}
-vocab.append("Ġt")
-```
-
-To continue, we need to apply that merge in our `splits` dictionary. Let's write another function for this:
-
-```python
-def merge_pair(a, b, splits):
- for word in word_freqs:
- split = splits[word]
- if len(split) == 1:
- continue
-
- i = 0
- while i < len(split) - 1:
- if split[i] == a and split[i + 1] == b:
- split = split[:i] + [a + b] + split[i + 2 :]
- else:
- i += 1
- splits[word] = split
- return splits
-```
-
-And we can have a look at the result of the first merge:
-
-```py
-splits = merge_pair("Ġ", "t", splits)
-print(splits["Ġtrained"])
-```
-
-```python out
-['Ġt', 'r', 'a', 'i', 'n', 'e', 'd']
-```
-
-Now we have everything we need to loop until we have learned all the merges we want. Let's aim for a vocab size of 50:
-
-```python
-vocab_size = 50
-
-while len(vocab) < vocab_size:
- pair_freqs = compute_pair_freqs(splits)
- best_pair = ""
- max_freq = None
- for pair, freq in pair_freqs.items():
- if max_freq is None or max_freq < freq:
- best_pair = pair
- max_freq = freq
- splits = merge_pair(*best_pair, splits)
- merges[best_pair] = best_pair[0] + best_pair[1]
- vocab.append(best_pair[0] + best_pair[1])
-```
-
-As a result, we've learned 19 merge rules (the initial vocabulary had a size of 31 -- 30 characters in the alphabet, plus the special token):
-
-```py
-print(merges)
-```
-
-```python out
-{('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en',
- ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok',
- ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe',
- ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'}
-```
-
-And the vocabulary is composed of the special token, the initial alphabet, and all the results of the merges:
-
-```py
-print(vocab)
-```
-
-```python out
-['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o',
- 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se',
- 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni']
-```
-
-> [!TIP]
-> 💡 Using `train_new_from_iterator()` on the same corpus won't result in the exact same vocabulary. This is because when there is a choice of the most frequent pair, we selected the first one encountered, while the 🤗 Tokenizers library selects the first one based on its inner IDs.
-
-To tokenize a new text, we pre-tokenize it, split it, then apply all the merge rules learned:
-
-```python
-def tokenize(text):
- pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
- pre_tokenized_text = [word for word, offset in pre_tokenize_result]
- splits = [[l for l in word] for word in pre_tokenized_text]
- for pair, merge in merges.items():
- for idx, split in enumerate(splits):
- i = 0
- while i < len(split) - 1:
- if split[i] == pair[0] and split[i + 1] == pair[1]:
- split = split[:i] + [merge] + split[i + 2 :]
- else:
- i += 1
- splits[idx] = split
-
- return sum(splits, [])
-```
-
-We can try this on any text composed of characters in the alphabet:
-
-```py
-tokenize("This is not a token.")
-```
-
-```python out
-['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġa', 'Ġtoken', '.']
-```
-
-> [!WARNING]
-> ⚠️ Our implementation will throw an error if there is an unknown character since we didn't do anything to handle them. GPT-2 doesn't actually have an unknown token (it's impossible to get an unknown character when using byte-level BPE), but this could happen here because we did not include all the possible bytes in the initial vocabulary. This aspect of BPE is beyond the scope of this section, so we've left the details out.
-
+# Byte-Pair Encoding tokenization[[byte-pair-encoding-tokenization]]
+
+
+
+Byte-Pair Encoding (BPE) was initially developed as an algorithm to compress texts, and then used by OpenAI for tokenization when pretraining the GPT model. It's used by a lot of Transformer models, including GPT, GPT-2, RoBERTa, BART, and DeBERTa.
+
+
+
+> [!TIP]
+> 💡 This section covers BPE in depth, going as far as showing a full implementation. You can skip to the end if you just want a general overview of the tokenization algorithm.
+
+## Training algorithm[[training-algorithm]]
+
+BPE training starts by computing the unique set of words used in the corpus (after the normalization and pre-tokenization steps are completed), then building the vocabulary by taking all the symbols used to write those words. As a very simple example, let's say our corpus uses these five words:
+
+```
+"hug", "pug", "pun", "bun", "hugs"
+```
+
+The base vocabulary will then be `["b", "g", "h", "n", "p", "s", "u"]`. For real-world cases, that base vocabulary will contain all the ASCII characters, at the very least, and probably some Unicode characters as well. If an example you are tokenizing uses a character that is not in the training corpus, that character will be converted to the unknown token. That's one reason why lots of NLP models are very bad at analyzing content with emojis, for instance.
+
+> [!TIP]
+> The GPT-2 and RoBERTa tokenizers (which are pretty similar) have a clever way to deal with this: they don't look at words as being written with Unicode characters, but with bytes. This way the base vocabulary has a small size (256), but every character you can think of will still be included and not end up being converted to the unknown token. This trick is called *byte-level BPE*.
+
+After getting this base vocabulary, we add new tokens until the desired vocabulary size is reached by learning *merges*, which are rules to merge two elements of the existing vocabulary together into a new one. So, at the beginning these merges will create tokens with two characters, and then, as training progresses, longer subwords.
+
+At any step during the tokenizer training, the BPE algorithm will search for the most frequent pair of existing tokens (by "pair," here we mean two consecutive tokens in a word). That most frequent pair is the one that will be merged, and we rinse and repeat for the next step.
+
+Going back to our previous example, let's assume the words had the following frequencies:
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+meaning `"hug"` was present 10 times in the corpus, `"pug"` 5 times, `"pun"` 12 times, `"bun"` 4 times, and `"hugs"` 5 times. We start the training by splitting each word into characters (the ones that form our initial vocabulary) so we can see each word as a list of tokens:
+
+```
+("h" "u" "g", 10), ("p" "u" "g", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "u" "g" "s", 5)
+```
+
+Then we look at pairs. The pair `("h", "u")` is present in the words `"hug"` and `"hugs"`, so 15 times total in the corpus. It's not the most frequent pair, though: that honor belongs to `("u", "g")`, which is present in `"hug"`, `"pug"`, and `"hugs"`, for a grand total of 20 times in the vocabulary.
+
+Thus, the first merge rule learned by the tokenizer is `("u", "g") -> "ug"`, which means that `"ug"` will be added to the vocabulary, and the pair should be merged in all the words of the corpus. At the end of this stage, the vocabulary and corpus look like this:
+
+```
+Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug"]
+Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "u" "n", 12), ("b" "u" "n", 4), ("h" "ug" "s", 5)
+```
+
+Now we have some pairs that result in a token longer than two characters: the pair `("h", "ug")`, for instance (present 15 times in the corpus). The most frequent pair at this stage is `("u", "n")`, however, present 16 times in the corpus, so the second merge rule learned is `("u", "n") -> "un"`. Adding that to the vocabulary and merging all existing occurrences leads us to:
+
+```
+Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un"]
+Corpus: ("h" "ug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("h" "ug" "s", 5)
+```
+
+Now the most frequent pair is `("h", "ug")`, so we learn the merge rule `("h", "ug") -> "hug"`, which gives us our first three-letter token. After the merge, the corpus looks like this:
+
+```
+Vocabulary: ["b", "g", "h", "n", "p", "s", "u", "ug", "un", "hug"]
+Corpus: ("hug", 10), ("p" "ug", 5), ("p" "un", 12), ("b" "un", 4), ("hug" "s", 5)
+```
+
+And we continue like this until we reach the desired vocabulary size.
+
+> [!TIP]
+> ✏️ **Now your turn!** What do you think the next merge rule will be?
+
+## Tokenization algorithm[[tokenization-algorithm]]
+
+Tokenization follows the training process closely, in the sense that new inputs are tokenized by applying the following steps:
+
+1. Normalization
+2. Pre-tokenization
+3. Splitting the words into individual characters
+4. Applying the merge rules learned in order on those splits
+
+Let's take the example we used during training, with the three merge rules learned:
+
+```
+("u", "g") -> "ug"
+("u", "n") -> "un"
+("h", "ug") -> "hug"
+```
+
+The word `"bug"` will be tokenized as `["b", "ug"]`. `"mug"`, however, will be tokenized as `["[UNK]", "ug"]` since the letter `"m"` was not in the base vocabulary. Likewise, the word `"thug"` will be tokenized as `["[UNK]", "hug"]`: the letter `"t"` is not in the base vocabulary, and applying the merge rules results first in `"u"` and `"g"` being merged and then `"h"` and `"ug"` being merged.
+
+> [!TIP]
+> ✏️ **Now your turn!** How do you think the word `"unhug"` will be tokenized?
+
+## Implementing BPE[[implementing-bpe]]
+
+Now let's take a look at an implementation of the BPE algorithm. This won't be an optimized version you can actually use on a big corpus; we just want to show you the code so you can understand the algorithm a little bit better.
+
+First we need a corpus, so let's create a simple one with a few sentences:
+
+```python
+corpus = [
+ "This is the Hugging Face Course.",
+ "This chapter is about tokenization.",
+ "This section shows several tokenizer algorithms.",
+ "Hopefully, you will be able to understand how they are trained and generate tokens.",
+]
+```
+
+Next, we need to pre-tokenize that corpus into words. Since we are replicating a BPE tokenizer (like GPT-2), we will use the `gpt2` tokenizer for the pre-tokenization:
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("gpt2")
+```
+
+Then we compute the frequencies of each word in the corpus as we do the pre-tokenization:
+
+```python
+from collections import defaultdict
+
+word_freqs = defaultdict(int)
+
+for text in corpus:
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ new_words = [word for word, offset in words_with_offsets]
+ for word in new_words:
+ word_freqs[word] += 1
+
+print(word_freqs)
+```
+
+```python out
+defaultdict(int, {'This': 3, 'Ġis': 2, 'Ġthe': 1, 'ĠHugging': 1, 'ĠFace': 1, 'ĠCourse': 1, '.': 4, 'Ġchapter': 1,
+ 'Ġabout': 1, 'Ġtokenization': 1, 'Ġsection': 1, 'Ġshows': 1, 'Ġseveral': 1, 'Ġtokenizer': 1, 'Ġalgorithms': 1,
+ 'Hopefully': 1, ',': 1, 'Ġyou': 1, 'Ġwill': 1, 'Ġbe': 1, 'Ġable': 1, 'Ġto': 1, 'Ġunderstand': 1, 'Ġhow': 1,
+ 'Ġthey': 1, 'Ġare': 1, 'Ġtrained': 1, 'Ġand': 1, 'Ġgenerate': 1, 'Ġtokens': 1})
+```
+
+The next step is to compute the base vocabulary, formed by all the characters used in the corpus:
+
+```python
+alphabet = []
+
+for word in word_freqs.keys():
+ for letter in word:
+ if letter not in alphabet:
+ alphabet.append(letter)
+alphabet.sort()
+
+print(alphabet)
+```
+
+```python out
+[ ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o', 'p', 'r', 's',
+ 't', 'u', 'v', 'w', 'y', 'z', 'Ġ']
+```
+
+We also add the special tokens used by the model at the beginning of that vocabulary. In the case of GPT-2, the only special token is `"<|endoftext|>"`:
+
+```python
+vocab = ["<|endoftext|>"] + alphabet.copy()
+```
+
+We now need to split each word into individual characters, to be able to start training:
+
+```python
+splits = {word: [c for c in word] for word in word_freqs.keys()}
+```
+
+Now that we are ready for training, let's write a function that computes the frequency of each pair. We'll need to use this at each step of the training:
+
+```python
+def compute_pair_freqs(splits):
+ pair_freqs = defaultdict(int)
+ for word, freq in word_freqs.items():
+ split = splits[word]
+ if len(split) == 1:
+ continue
+ for i in range(len(split) - 1):
+ pair = (split[i], split[i + 1])
+ pair_freqs[pair] += freq
+ return pair_freqs
+```
+
+Let's have a look at a part of this dictionary after the initial splits:
+
+```python
+pair_freqs = compute_pair_freqs(splits)
+
+for i, key in enumerate(pair_freqs.keys()):
+ print(f"{key}: {pair_freqs[key]}")
+ if i >= 5:
+ break
+```
+
+```python out
+('T', 'h'): 3
+('h', 'i'): 3
+('i', 's'): 5
+('Ġ', 'i'): 2
+('Ġ', 't'): 7
+('t', 'h'): 3
+```
+
+Now, finding the most frequent pair only takes a quick loop:
+
+```python
+best_pair = ""
+max_freq = None
+
+for pair, freq in pair_freqs.items():
+ if max_freq is None or max_freq < freq:
+ best_pair = pair
+ max_freq = freq
+
+print(best_pair, max_freq)
+```
+
+```python out
+('Ġ', 't') 7
+```
+
+So the first merge to learn is `('Ġ', 't') -> 'Ġt'`, and we add `'Ġt'` to the vocabulary:
+
+```python
+merges = {("Ġ", "t"): "Ġt"}
+vocab.append("Ġt")
+```
+
+To continue, we need to apply that merge in our `splits` dictionary. Let's write another function for this:
+
+```python
+def merge_pair(a, b, splits):
+ for word in word_freqs:
+ split = splits[word]
+ if len(split) == 1:
+ continue
+
+ i = 0
+ while i < len(split) - 1:
+ if split[i] == a and split[i + 1] == b:
+ split = split[:i] + [a + b] + split[i + 2 :]
+ else:
+ i += 1
+ splits[word] = split
+ return splits
+```
+
+And we can have a look at the result of the first merge:
+
+```py
+splits = merge_pair("Ġ", "t", splits)
+print(splits["Ġtrained"])
+```
+
+```python out
+['Ġt', 'r', 'a', 'i', 'n', 'e', 'd']
+```
+
+Now we have everything we need to loop until we have learned all the merges we want. Let's aim for a vocab size of 50:
+
+```python
+vocab_size = 50
+
+while len(vocab) < vocab_size:
+ pair_freqs = compute_pair_freqs(splits)
+ best_pair = ""
+ max_freq = None
+ for pair, freq in pair_freqs.items():
+ if max_freq is None or max_freq < freq:
+ best_pair = pair
+ max_freq = freq
+ splits = merge_pair(*best_pair, splits)
+ merges[best_pair] = best_pair[0] + best_pair[1]
+ vocab.append(best_pair[0] + best_pair[1])
+```
+
+As a result, we've learned 19 merge rules (the initial vocabulary had a size of 31 -- 30 characters in the alphabet, plus the special token):
+
+```py
+print(merges)
+```
+
+```python out
+{('Ġ', 't'): 'Ġt', ('i', 's'): 'is', ('e', 'r'): 'er', ('Ġ', 'a'): 'Ġa', ('Ġt', 'o'): 'Ġto', ('e', 'n'): 'en',
+ ('T', 'h'): 'Th', ('Th', 'is'): 'This', ('o', 'u'): 'ou', ('s', 'e'): 'se', ('Ġto', 'k'): 'Ġtok',
+ ('Ġtok', 'en'): 'Ġtoken', ('n', 'd'): 'nd', ('Ġ', 'is'): 'Ġis', ('Ġt', 'h'): 'Ġth', ('Ġth', 'e'): 'Ġthe',
+ ('i', 'n'): 'in', ('Ġa', 'b'): 'Ġab', ('Ġtoken', 'i'): 'Ġtokeni'}
+```
+
+And the vocabulary is composed of the special token, the initial alphabet, and all the results of the merges:
+
+```py
+print(vocab)
+```
+
+```python out
+['<|endoftext|>', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'k', 'l', 'm', 'n', 'o',
+ 'p', 'r', 's', 't', 'u', 'v', 'w', 'y', 'z', 'Ġ', 'Ġt', 'is', 'er', 'Ġa', 'Ġto', 'en', 'Th', 'This', 'ou', 'se',
+ 'Ġtok', 'Ġtoken', 'nd', 'Ġis', 'Ġth', 'Ġthe', 'in', 'Ġab', 'Ġtokeni']
+```
+
+> [!TIP]
+> 💡 Using `train_new_from_iterator()` on the same corpus won't result in the exact same vocabulary. This is because when there is a choice of the most frequent pair, we selected the first one encountered, while the 🤗 Tokenizers library selects the first one based on its inner IDs.
+
+To tokenize a new text, we pre-tokenize it, split it, then apply all the merge rules learned:
+
+```python
+def tokenize(text):
+ pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ pre_tokenized_text = [word for word, offset in pre_tokenize_result]
+ splits = [[l for l in word] for word in pre_tokenized_text]
+ for pair, merge in merges.items():
+ for idx, split in enumerate(splits):
+ i = 0
+ while i < len(split) - 1:
+ if split[i] == pair[0] and split[i + 1] == pair[1]:
+ split = split[:i] + [merge] + split[i + 2 :]
+ else:
+ i += 1
+ splits[idx] = split
+
+ return sum(splits, [])
+```
+
+We can try this on any text composed of characters in the alphabet:
+
+```py
+tokenize("This is not a token.")
+```
+
+```python out
+['This', 'Ġis', 'Ġ', 'n', 'o', 't', 'Ġa', 'Ġtoken', '.']
+```
+
+> [!WARNING]
+> ⚠️ Our implementation will throw an error if there is an unknown character since we didn't do anything to handle them. GPT-2 doesn't actually have an unknown token (it's impossible to get an unknown character when using byte-level BPE), but this could happen here because we did not include all the possible bytes in the initial vocabulary. This aspect of BPE is beyond the scope of this section, so we've left the details out.
+
That's it for the BPE algorithm! Next, we'll have a look at WordPiece.
\ No newline at end of file
diff --git a/chapters/en/chapter6/6.mdx b/chapters/en/chapter6/6.mdx
index 96957b763..e965e8b59 100644
--- a/chapters/en/chapter6/6.mdx
+++ b/chapters/en/chapter6/6.mdx
@@ -1,359 +1,359 @@
-# WordPiece tokenization[[wordpiece-tokenization]]
-
-
-
-WordPiece is the tokenization algorithm Google developed to pretrain BERT. It has since been reused in quite a few Transformer models based on BERT, such as DistilBERT, MobileBERT, Funnel Transformers, and MPNET. It's very similar to BPE in terms of the training, but the actual tokenization is done differently.
-
-
-
-> [!TIP]
-> 💡 This section covers WordPiece in depth, going as far as showing a full implementation. You can skip to the end if you just want a general overview of the tokenization algorithm.
-
-## Training algorithm[[training-algorithm]]
-
-> [!WARNING]
-> ⚠️ Google never open-sourced its implementation of the training algorithm of WordPiece, so what follows is our best guess based on the published literature. It may not be 100% accurate.
-
-Like BPE, WordPiece starts from a small vocabulary including the special tokens used by the model and the initial alphabet. Since it identifies subwords by adding a prefix (like `##` for BERT), each word is initially split by adding that prefix to all the characters inside the word. So, for instance, `"word"` gets split like this:
-
-```
-w ##o ##r ##d
-```
-
-Thus, the initial alphabet contains all the characters present at the beginning of a word and the characters present inside a word preceded by the WordPiece prefix.
-
-Then, again like BPE, WordPiece learns merge rules. The main difference is the way the pair to be merged is selected. Instead of selecting the most frequent pair, WordPiece computes a score for each pair, using the following formula:
-
-$$\mathrm{score} = (\mathrm{freq\_of\_pair}) / (\mathrm{freq\_of\_first\_element} \times \mathrm{freq\_of\_second\_element})$$
-
-By dividing the frequency of the pair by the product of the frequencies of each of its parts, the algorithm prioritizes the merging of pairs where the individual parts are less frequent in the vocabulary. For instance, it won't necessarily merge `("un", "##able")` even if that pair occurs very frequently in the vocabulary, because the two pairs `"un"` and `"##able"` will likely each appear in a lot of other words and have a high frequency. In contrast, a pair like `("hu", "##gging")` will probably be merged faster (assuming the word "hugging" appears often in the vocabulary) since `"hu"` and `"##gging"` are likely to be less frequent individually.
-
-Let's look at the same vocabulary we used in the BPE training example:
-
-```
-("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
-```
-
-The splits here will be:
-
-```
-("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5)
-```
-
-so the initial vocabulary will be `["b", "h", "p", "##g", "##n", "##s", "##u"]` (if we forget about special tokens for now). The most frequent pair is `("##u", "##g")` (present 20 times), but the individual frequency of `"##u"` is very high, so its score is not the highest (it's 1 / 36). All pairs with a `"##u"` actually have that same score (1 / 36), so the best score goes to the pair `("##g", "##s")` -- the only one without a `"##u"` -- at 1 / 20, and the first merge learned is `("##g", "##s") -> ("##gs")`.
-
-Note that when we merge, we remove the `##` between the two tokens, so we add `"##gs"` to the vocabulary and apply the merge in the words of the corpus:
-
-```
-Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]
-Corpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)
-```
-
-At this point, `"##u"` is in all the possible pairs, so they all end up with the same score. Let's say that in this case, the first pair is merged, so `("h", "##u") -> "hu"`. This takes us to:
-
-```
-Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu"]
-Corpus: ("hu" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
-```
-
-Then the next best score is shared by `("hu", "##g")` and `("hu", "##gs")` (with 1/15, compared to 1/21 for all the other pairs), so the first pair with the biggest score is merged:
-
-```
-Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]
-Corpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
-```
-
-and we continue like this until we reach the desired vocabulary size.
-
-> [!TIP]
-> ✏️ **Now your turn!** What will the next merge rule be?
-
-## Tokenization algorithm[[tokenization-algorithm]]
-
-Tokenization differs in WordPiece and BPE in that WordPiece only saves the final vocabulary, not the merge rules learned. Starting from the word to tokenize, WordPiece finds the longest subword that is in the vocabulary, then splits on it. For instance, if we use the vocabulary learned in the example above, for the word `"hugs"` the longest subword starting from the beginning that is inside the vocabulary is `"hug"`, so we split there and get `["hug", "##s"]`. We then continue with `"##s"`, which is in the vocabulary, so the tokenization of `"hugs"` is `["hug", "##s"]`.
-
-With BPE, we would have applied the merges learned in order and tokenized this as `["hu", "##gs"]`, so the encoding is different.
-
-As another example, let's see how the word `"bugs"` would be tokenized. `"b"` is the longest subword starting at the beginning of the word that is in the vocabulary, so we split there and get `["b", "##ugs"]`. Then `"##u"` is the longest subword starting at the beginning of `"##ugs"` that is in the vocabulary, so we split there and get `["b", "##u, "##gs"]`. Finally, `"##gs"` is in the vocabulary, so this last list is the tokenization of `"bugs"`.
-
-When the tokenization gets to a stage where it's not possible to find a subword in the vocabulary, the whole word is tokenized as unknown -- so, for instance, `"mug"` would be tokenized as `["[UNK]"]`, as would `"bum"` (even if we can begin with `"b"` and `"##u"`, `"##m"` is not the vocabulary, and the resulting tokenization will just be `["[UNK]"]`, not `["b", "##u", "[UNK]"]`). This is another difference from BPE, which would only classify the individual characters not in the vocabulary as unknown.
-
-> [!TIP]
-> ✏️ **Now your turn!** How will the word `"pugs"` be tokenized?
-
-## Implementing WordPiece[[implementing-wordpiece]]
-
-Now let's take a look at an implementation of the WordPiece algorithm. Like with BPE, this is just pedagogical, and you won't able to use this on a big corpus.
-
-We will use the same corpus as in the BPE example:
-
-```python
-corpus = [
- "This is the Hugging Face Course.",
- "This chapter is about tokenization.",
- "This section shows several tokenizer algorithms.",
- "Hopefully, you will be able to understand how they are trained and generate tokens.",
-]
-```
-
-First, we need to pre-tokenize the corpus into words. Since we are replicating a WordPiece tokenizer (like BERT), we will use the `bert-base-cased` tokenizer for the pre-tokenization:
-
-```python
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
-```
-
-Then we compute the frequencies of each word in the corpus as we do the pre-tokenization:
-
-```python
-from collections import defaultdict
-
-word_freqs = defaultdict(int)
-for text in corpus:
- words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
- new_words = [word for word, offset in words_with_offsets]
- for word in new_words:
- word_freqs[word] += 1
-
-word_freqs
-```
-
-```python out
-defaultdict(
- int, {'This': 3, 'is': 2, 'the': 1, 'Hugging': 1, 'Face': 1, 'Course': 1, '.': 4, 'chapter': 1, 'about': 1,
- 'tokenization': 1, 'section': 1, 'shows': 1, 'several': 1, 'tokenizer': 1, 'algorithms': 1, 'Hopefully': 1,
- ',': 1, 'you': 1, 'will': 1, 'be': 1, 'able': 1, 'to': 1, 'understand': 1, 'how': 1, 'they': 1, 'are': 1,
- 'trained': 1, 'and': 1, 'generate': 1, 'tokens': 1})
-```
-
-As we saw before, the alphabet is the unique set composed of all the first letters of words, and all the other letters that appear in words prefixed by `##`:
-
-```python
-alphabet = []
-for word in word_freqs.keys():
- if word[0] not in alphabet:
- alphabet.append(word[0])
- for letter in word[1:]:
- if f"##{letter}" not in alphabet:
- alphabet.append(f"##{letter}")
-
-alphabet.sort()
-alphabet
-
-print(alphabet)
-```
-
-```python out
-['##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', '##l', '##m', '##n', '##o', '##p', '##r', '##s',
- '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u',
- 'w', 'y']
-```
-
-We also add the special tokens used by the model at the beginning of that vocabulary. In the case of BERT, it's the list `["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]`:
-
-```python
-vocab = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + alphabet.copy()
-```
-
-Next we need to split each word, with all the letters that are not the first prefixed by `##`:
-
-```python
-splits = {
- word: [c if i == 0 else f"##{c}" for i, c in enumerate(word)]
- for word in word_freqs.keys()
-}
-```
-
-Now that we are ready for training, let's write a function that computes the score of each pair. We'll need to use this at each step of the training:
-
-```python
-def compute_pair_scores(splits):
- letter_freqs = defaultdict(int)
- pair_freqs = defaultdict(int)
- for word, freq in word_freqs.items():
- split = splits[word]
- if len(split) == 1:
- letter_freqs[split[0]] += freq
- continue
- for i in range(len(split) - 1):
- pair = (split[i], split[i + 1])
- letter_freqs[split[i]] += freq
- pair_freqs[pair] += freq
- letter_freqs[split[-1]] += freq
-
- scores = {
- pair: freq / (letter_freqs[pair[0]] * letter_freqs[pair[1]])
- for pair, freq in pair_freqs.items()
- }
- return scores
-```
-
-Let's have a look at a part of this dictionary after the initial splits:
-
-```python
-pair_scores = compute_pair_scores(splits)
-for i, key in enumerate(pair_scores.keys()):
- print(f"{key}: {pair_scores[key]}")
- if i >= 5:
- break
-```
-
-```python out
-('T', '##h'): 0.125
-('##h', '##i'): 0.03409090909090909
-('##i', '##s'): 0.02727272727272727
-('i', '##s'): 0.1
-('t', '##h'): 0.03571428571428571
-('##h', '##e'): 0.011904761904761904
-```
-
-Now, finding the pair with the best score only takes a quick loop:
-
-```python
-best_pair = ""
-max_score = None
-for pair, score in pair_scores.items():
- if max_score is None or max_score < score:
- best_pair = pair
- max_score = score
-
-print(best_pair, max_score)
-```
-
-```python out
-('a', '##b') 0.2
-```
-
-So the first merge to learn is `('a', '##b') -> 'ab'`, and we add `'ab'` to the vocabulary:
-
-```python
-vocab.append("ab")
-```
-
-To continue, we need to apply that merge in our `splits` dictionary. Let's write another function for this:
-
-```python
-def merge_pair(a, b, splits):
- for word in word_freqs:
- split = splits[word]
- if len(split) == 1:
- continue
- i = 0
- while i < len(split) - 1:
- if split[i] == a and split[i + 1] == b:
- merge = a + b[2:] if b.startswith("##") else a + b
- split = split[:i] + [merge] + split[i + 2 :]
- else:
- i += 1
- splits[word] = split
- return splits
-```
-
-And we can have a look at the result of the first merge:
-
-```py
-splits = merge_pair("a", "##b", splits)
-splits["about"]
-```
-
-```python out
-['ab', '##o', '##u', '##t']
-```
-
-Now we have everything we need to loop until we have learned all the merges we want. Let's aim for a vocab size of 70:
-
-```python
-vocab_size = 70
-while len(vocab) < vocab_size:
- scores = compute_pair_scores(splits)
- best_pair, max_score = "", None
- for pair, score in scores.items():
- if max_score is None or max_score < score:
- best_pair = pair
- max_score = score
- splits = merge_pair(*best_pair, splits)
- new_token = (
- best_pair[0] + best_pair[1][2:]
- if best_pair[1].startswith("##")
- else best_pair[0] + best_pair[1]
- )
- vocab.append(new_token)
-```
-
-We can then look at the generated vocabulary:
-
-```py
-print(vocab)
-```
-
-```python out
-['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', '##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k',
- '##l', '##m', '##n', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H',
- 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', 'ab', '##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully',
- 'Th', 'ch', '##hm', 'cha', 'chap', 'chapt', '##thm', 'Hu', 'Hug', 'Hugg', 'sh', 'th', 'is', '##thms', '##za', '##zat',
- '##ut']
-```
-
-As we can see, compared to BPE, this tokenizer learns parts of words as tokens a bit faster.
-
-> [!TIP]
-> 💡 Using `train_new_from_iterator()` on the same corpus won't result in the exact same vocabulary. This is because the 🤗 Tokenizers library does not implement WordPiece for the training (since we are not completely sure of its internals), but uses BPE instead.
-
-To tokenize a new text, we pre-tokenize it, split it, then apply the tokenization algorithm on each word. That is, we look for the biggest subword starting at the beginning of the first word and split it, then we repeat the process on the second part, and so on for the rest of that word and the following words in the text:
-
-```python
-def encode_word(word):
- tokens = []
- while len(word) > 0:
- i = len(word)
- while i > 0 and word[:i] not in vocab:
- i -= 1
- if i == 0:
- return ["[UNK]"]
- tokens.append(word[:i])
- word = word[i:]
- if len(word) > 0:
- word = f"##{word}"
- return tokens
-```
-
-Let's test it on one word that's in the vocabulary, and another that isn't:
-
-```python
-print(encode_word("Hugging"))
-print(encode_word("HOgging"))
-```
-
-```python out
-['Hugg', '##i', '##n', '##g']
-['[UNK]']
-```
-
-Now, let's write a function that tokenizes a text:
-
-```python
-def tokenize(text):
- pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
- pre_tokenized_text = [word for word, offset in pre_tokenize_result]
- encoded_words = [encode_word(word) for word in pre_tokenized_text]
- return sum(encoded_words, [])
-```
-
-We can try it on any text:
-
-```python
-tokenize("This is the Hugging Face course!")
-```
-
-```python out
-['Th', '##i', '##s', 'is', 'th', '##e', 'Hugg', '##i', '##n', '##g', 'Fac', '##e', 'c', '##o', '##u', '##r', '##s',
- '##e', '[UNK]']
-```
-
-That's it for the WordPiece algorithm! Now let's take a look at Unigram.
+# WordPiece tokenization[[wordpiece-tokenization]]
+
+
+
+WordPiece is the tokenization algorithm Google developed to pretrain BERT. It has since been reused in quite a few Transformer models based on BERT, such as DistilBERT, MobileBERT, Funnel Transformers, and MPNET. It's very similar to BPE in terms of the training, but the actual tokenization is done differently.
+
+
+
+> [!TIP]
+> 💡 This section covers WordPiece in depth, going as far as showing a full implementation. You can skip to the end if you just want a general overview of the tokenization algorithm.
+
+## Training algorithm[[training-algorithm]]
+
+> [!WARNING]
+> ⚠️ Google never open-sourced its implementation of the training algorithm of WordPiece, so what follows is our best guess based on the published literature. It may not be 100% accurate.
+
+Like BPE, WordPiece starts from a small vocabulary including the special tokens used by the model and the initial alphabet. Since it identifies subwords by adding a prefix (like `##` for BERT), each word is initially split by adding that prefix to all the characters inside the word. So, for instance, `"word"` gets split like this:
+
+```
+w ##o ##r ##d
+```
+
+Thus, the initial alphabet contains all the characters present at the beginning of a word and the characters present inside a word preceded by the WordPiece prefix.
+
+Then, again like BPE, WordPiece learns merge rules. The main difference is the way the pair to be merged is selected. Instead of selecting the most frequent pair, WordPiece computes a score for each pair, using the following formula:
+
+$$\mathrm{score} = (\mathrm{freq\_of\_pair}) / (\mathrm{freq\_of\_first\_element} \times \mathrm{freq\_of\_second\_element})$$
+
+By dividing the frequency of the pair by the product of the frequencies of each of its parts, the algorithm prioritizes the merging of pairs where the individual parts are less frequent in the vocabulary. For instance, it won't necessarily merge `("un", "##able")` even if that pair occurs very frequently in the vocabulary, because the two pairs `"un"` and `"##able"` will likely each appear in a lot of other words and have a high frequency. In contrast, a pair like `("hu", "##gging")` will probably be merged faster (assuming the word "hugging" appears often in the vocabulary) since `"hu"` and `"##gging"` are likely to be less frequent individually.
+
+Let's look at the same vocabulary we used in the BPE training example:
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+The splits here will be:
+
+```
+("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##g" "##s", 5)
+```
+
+so the initial vocabulary will be `["b", "h", "p", "##g", "##n", "##s", "##u"]` (if we forget about special tokens for now). The most frequent pair is `("##u", "##g")` (present 20 times), but the individual frequency of `"##u"` is very high, so its score is not the highest (it's 1 / 36). All pairs with a `"##u"` actually have that same score (1 / 36), so the best score goes to the pair `("##g", "##s")` -- the only one without a `"##u"` -- at 1 / 20, and the first merge learned is `("##g", "##s") -> ("##gs")`.
+
+Note that when we merge, we remove the `##` between the two tokens, so we add `"##gs"` to the vocabulary and apply the merge in the words of the corpus:
+
+```
+Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs"]
+Corpus: ("h" "##u" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("h" "##u" "##gs", 5)
+```
+
+At this point, `"##u"` is in all the possible pairs, so they all end up with the same score. Let's say that in this case, the first pair is merged, so `("h", "##u") -> "hu"`. This takes us to:
+
+```
+Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu"]
+Corpus: ("hu" "##g", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
+```
+
+Then the next best score is shared by `("hu", "##g")` and `("hu", "##gs")` (with 1/15, compared to 1/21 for all the other pairs), so the first pair with the biggest score is merged:
+
+```
+Vocabulary: ["b", "h", "p", "##g", "##n", "##s", "##u", "##gs", "hu", "hug"]
+Corpus: ("hug", 10), ("p" "##u" "##g", 5), ("p" "##u" "##n", 12), ("b" "##u" "##n", 4), ("hu" "##gs", 5)
+```
+
+and we continue like this until we reach the desired vocabulary size.
+
+> [!TIP]
+> ✏️ **Now your turn!** What will the next merge rule be?
+
+## Tokenization algorithm[[tokenization-algorithm]]
+
+Tokenization differs in WordPiece and BPE in that WordPiece only saves the final vocabulary, not the merge rules learned. Starting from the word to tokenize, WordPiece finds the longest subword that is in the vocabulary, then splits on it. For instance, if we use the vocabulary learned in the example above, for the word `"hugs"` the longest subword starting from the beginning that is inside the vocabulary is `"hug"`, so we split there and get `["hug", "##s"]`. We then continue with `"##s"`, which is in the vocabulary, so the tokenization of `"hugs"` is `["hug", "##s"]`.
+
+With BPE, we would have applied the merges learned in order and tokenized this as `["hu", "##gs"]`, so the encoding is different.
+
+As another example, let's see how the word `"bugs"` would be tokenized. `"b"` is the longest subword starting at the beginning of the word that is in the vocabulary, so we split there and get `["b", "##ugs"]`. Then `"##u"` is the longest subword starting at the beginning of `"##ugs"` that is in the vocabulary, so we split there and get `["b", "##u, "##gs"]`. Finally, `"##gs"` is in the vocabulary, so this last list is the tokenization of `"bugs"`.
+
+When the tokenization gets to a stage where it's not possible to find a subword in the vocabulary, the whole word is tokenized as unknown -- so, for instance, `"mug"` would be tokenized as `["[UNK]"]`, as would `"bum"` (even if we can begin with `"b"` and `"##u"`, `"##m"` is not the vocabulary, and the resulting tokenization will just be `["[UNK]"]`, not `["b", "##u", "[UNK]"]`). This is another difference from BPE, which would only classify the individual characters not in the vocabulary as unknown.
+
+> [!TIP]
+> ✏️ **Now your turn!** How will the word `"pugs"` be tokenized?
+
+## Implementing WordPiece[[implementing-wordpiece]]
+
+Now let's take a look at an implementation of the WordPiece algorithm. Like with BPE, this is just pedagogical, and you won't able to use this on a big corpus.
+
+We will use the same corpus as in the BPE example:
+
+```python
+corpus = [
+ "This is the Hugging Face Course.",
+ "This chapter is about tokenization.",
+ "This section shows several tokenizer algorithms.",
+ "Hopefully, you will be able to understand how they are trained and generate tokens.",
+]
+```
+
+First, we need to pre-tokenize the corpus into words. Since we are replicating a WordPiece tokenizer (like BERT), we will use the `bert-base-cased` tokenizer for the pre-tokenization:
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("bert-base-cased")
+```
+
+Then we compute the frequencies of each word in the corpus as we do the pre-tokenization:
+
+```python
+from collections import defaultdict
+
+word_freqs = defaultdict(int)
+for text in corpus:
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ new_words = [word for word, offset in words_with_offsets]
+ for word in new_words:
+ word_freqs[word] += 1
+
+word_freqs
+```
+
+```python out
+defaultdict(
+ int, {'This': 3, 'is': 2, 'the': 1, 'Hugging': 1, 'Face': 1, 'Course': 1, '.': 4, 'chapter': 1, 'about': 1,
+ 'tokenization': 1, 'section': 1, 'shows': 1, 'several': 1, 'tokenizer': 1, 'algorithms': 1, 'Hopefully': 1,
+ ',': 1, 'you': 1, 'will': 1, 'be': 1, 'able': 1, 'to': 1, 'understand': 1, 'how': 1, 'they': 1, 'are': 1,
+ 'trained': 1, 'and': 1, 'generate': 1, 'tokens': 1})
+```
+
+As we saw before, the alphabet is the unique set composed of all the first letters of words, and all the other letters that appear in words prefixed by `##`:
+
+```python
+alphabet = []
+for word in word_freqs.keys():
+ if word[0] not in alphabet:
+ alphabet.append(word[0])
+ for letter in word[1:]:
+ if f"##{letter}" not in alphabet:
+ alphabet.append(f"##{letter}")
+
+alphabet.sort()
+alphabet
+
+print(alphabet)
+```
+
+```python out
+['##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k', '##l', '##m', '##n', '##o', '##p', '##r', '##s',
+ '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H', 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u',
+ 'w', 'y']
+```
+
+We also add the special tokens used by the model at the beginning of that vocabulary. In the case of BERT, it's the list `["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"]`:
+
+```python
+vocab = ["[PAD]", "[UNK]", "[CLS]", "[SEP]", "[MASK]"] + alphabet.copy()
+```
+
+Next we need to split each word, with all the letters that are not the first prefixed by `##`:
+
+```python
+splits = {
+ word: [c if i == 0 else f"##{c}" for i, c in enumerate(word)]
+ for word in word_freqs.keys()
+}
+```
+
+Now that we are ready for training, let's write a function that computes the score of each pair. We'll need to use this at each step of the training:
+
+```python
+def compute_pair_scores(splits):
+ letter_freqs = defaultdict(int)
+ pair_freqs = defaultdict(int)
+ for word, freq in word_freqs.items():
+ split = splits[word]
+ if len(split) == 1:
+ letter_freqs[split[0]] += freq
+ continue
+ for i in range(len(split) - 1):
+ pair = (split[i], split[i + 1])
+ letter_freqs[split[i]] += freq
+ pair_freqs[pair] += freq
+ letter_freqs[split[-1]] += freq
+
+ scores = {
+ pair: freq / (letter_freqs[pair[0]] * letter_freqs[pair[1]])
+ for pair, freq in pair_freqs.items()
+ }
+ return scores
+```
+
+Let's have a look at a part of this dictionary after the initial splits:
+
+```python
+pair_scores = compute_pair_scores(splits)
+for i, key in enumerate(pair_scores.keys()):
+ print(f"{key}: {pair_scores[key]}")
+ if i >= 5:
+ break
+```
+
+```python out
+('T', '##h'): 0.125
+('##h', '##i'): 0.03409090909090909
+('##i', '##s'): 0.02727272727272727
+('i', '##s'): 0.1
+('t', '##h'): 0.03571428571428571
+('##h', '##e'): 0.011904761904761904
+```
+
+Now, finding the pair with the best score only takes a quick loop:
+
+```python
+best_pair = ""
+max_score = None
+for pair, score in pair_scores.items():
+ if max_score is None or max_score < score:
+ best_pair = pair
+ max_score = score
+
+print(best_pair, max_score)
+```
+
+```python out
+('a', '##b') 0.2
+```
+
+So the first merge to learn is `('a', '##b') -> 'ab'`, and we add `'ab'` to the vocabulary:
+
+```python
+vocab.append("ab")
+```
+
+To continue, we need to apply that merge in our `splits` dictionary. Let's write another function for this:
+
+```python
+def merge_pair(a, b, splits):
+ for word in word_freqs:
+ split = splits[word]
+ if len(split) == 1:
+ continue
+ i = 0
+ while i < len(split) - 1:
+ if split[i] == a and split[i + 1] == b:
+ merge = a + b[2:] if b.startswith("##") else a + b
+ split = split[:i] + [merge] + split[i + 2 :]
+ else:
+ i += 1
+ splits[word] = split
+ return splits
+```
+
+And we can have a look at the result of the first merge:
+
+```py
+splits = merge_pair("a", "##b", splits)
+splits["about"]
+```
+
+```python out
+['ab', '##o', '##u', '##t']
+```
+
+Now we have everything we need to loop until we have learned all the merges we want. Let's aim for a vocab size of 70:
+
+```python
+vocab_size = 70
+while len(vocab) < vocab_size:
+ scores = compute_pair_scores(splits)
+ best_pair, max_score = "", None
+ for pair, score in scores.items():
+ if max_score is None or max_score < score:
+ best_pair = pair
+ max_score = score
+ splits = merge_pair(*best_pair, splits)
+ new_token = (
+ best_pair[0] + best_pair[1][2:]
+ if best_pair[1].startswith("##")
+ else best_pair[0] + best_pair[1]
+ )
+ vocab.append(new_token)
+```
+
+We can then look at the generated vocabulary:
+
+```py
+print(vocab)
+```
+
+```python out
+['[PAD]', '[UNK]', '[CLS]', '[SEP]', '[MASK]', '##a', '##b', '##c', '##d', '##e', '##f', '##g', '##h', '##i', '##k',
+ '##l', '##m', '##n', '##o', '##p', '##r', '##s', '##t', '##u', '##v', '##w', '##y', '##z', ',', '.', 'C', 'F', 'H',
+ 'T', 'a', 'b', 'c', 'g', 'h', 'i', 's', 't', 'u', 'w', 'y', 'ab', '##fu', 'Fa', 'Fac', '##ct', '##ful', '##full', '##fully',
+ 'Th', 'ch', '##hm', 'cha', 'chap', 'chapt', '##thm', 'Hu', 'Hug', 'Hugg', 'sh', 'th', 'is', '##thms', '##za', '##zat',
+ '##ut']
+```
+
+As we can see, compared to BPE, this tokenizer learns parts of words as tokens a bit faster.
+
+> [!TIP]
+> 💡 Using `train_new_from_iterator()` on the same corpus won't result in the exact same vocabulary. This is because the 🤗 Tokenizers library does not implement WordPiece for the training (since we are not completely sure of its internals), but uses BPE instead.
+
+To tokenize a new text, we pre-tokenize it, split it, then apply the tokenization algorithm on each word. That is, we look for the biggest subword starting at the beginning of the first word and split it, then we repeat the process on the second part, and so on for the rest of that word and the following words in the text:
+
+```python
+def encode_word(word):
+ tokens = []
+ while len(word) > 0:
+ i = len(word)
+ while i > 0 and word[:i] not in vocab:
+ i -= 1
+ if i == 0:
+ return ["[UNK]"]
+ tokens.append(word[:i])
+ word = word[i:]
+ if len(word) > 0:
+ word = f"##{word}"
+ return tokens
+```
+
+Let's test it on one word that's in the vocabulary, and another that isn't:
+
+```python
+print(encode_word("Hugging"))
+print(encode_word("HOgging"))
+```
+
+```python out
+['Hugg', '##i', '##n', '##g']
+['[UNK]']
+```
+
+Now, let's write a function that tokenizes a text:
+
+```python
+def tokenize(text):
+ pre_tokenize_result = tokenizer._tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ pre_tokenized_text = [word for word, offset in pre_tokenize_result]
+ encoded_words = [encode_word(word) for word in pre_tokenized_text]
+ return sum(encoded_words, [])
+```
+
+We can try it on any text:
+
+```python
+tokenize("This is the Hugging Face course!")
+```
+
+```python out
+['Th', '##i', '##s', 'is', 'th', '##e', 'Hugg', '##i', '##n', '##g', 'Fac', '##e', 'c', '##o', '##u', '##r', '##s',
+ '##e', '[UNK]']
+```
+
+That's it for the WordPiece algorithm! Now let's take a look at Unigram.
diff --git a/chapters/en/chapter6/7.mdx b/chapters/en/chapter6/7.mdx
index deb1c9ee4..5ed0e8bf6 100644
--- a/chapters/en/chapter6/7.mdx
+++ b/chapters/en/chapter6/7.mdx
@@ -1,371 +1,371 @@
-# Unigram tokenization[[unigram-tokenization]]
-
-
-
-The Unigram algorithm is used in combination with [SentencePiece](https://huggingface.co/papers/1808.06226), which is the tokenization algorithm used by models like AlBERT, T5, mBART, Big Bird, and XLNet.
-
-SentencePiece addresses the fact that not all languages use spaces to separate words. Instead, SentencePiece treats the input as a raw input stream which includes the space in the set of characters to use. Then it can use the Unigram algorithm to construct the appropriate vocabulary.
-
-
-
-> [!TIP]
-> 💡 This section covers Unigram in depth, going as far as showing a full implementation. You can skip to the end if you just want a general overview of the tokenization algorithm.
-
-## Training algorithm[[training-algorithm]]
-
-Compared to BPE and WordPiece, Unigram works in the other direction: it starts from a big vocabulary and removes tokens from it until it reaches the desired vocabulary size. There are several options to use to build that base vocabulary: we can take the most common substrings in pre-tokenized words, for instance, or apply BPE on the initial corpus with a large vocabulary size.
-
-At each step of the training, the Unigram algorithm computes a loss over the corpus given the current vocabulary. Then, for each symbol in the vocabulary, the algorithm computes how much the overall loss would increase if the symbol was removed, and looks for the symbols that would increase it the least. Those symbols have a lower effect on the overall loss over the corpus, so in a sense they are "less needed" and are the best candidates for removal.
-
-This is all a very costly operation, so we don't just remove the single symbol associated with the lowest loss increase, but the \\(p\\) (\\(p\\) being a hyperparameter you can control, usually 10 or 20) percent of the symbols associated with the lowest loss increase. This process is then repeated until the vocabulary has reached the desired size.
-
-Note that we never remove the base characters, to make sure any word can be tokenized.
-
-Now, this is still a bit vague: the main part of the algorithm is to compute a loss over the corpus and see how it changes when we remove some tokens from the vocabulary, but we haven't explained how to do this yet. This step relies on the tokenization algorithm of a Unigram model, so we'll dive into this next.
-
-We'll reuse the corpus from the previous examples:
-
-```
-("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
-```
-
-and for this example, we will take all strict substrings for the initial vocabulary :
-
-```
-["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
-```
-
-## Tokenization algorithm[[tokenization-algorithm]]
-
-A Unigram model is a type of language model that considers each token to be independent of the tokens before it. It's the simplest language model, in the sense that the probability of token X given the previous context is just the probability of token X. So, if we used a Unigram language model to generate text, we would always predict the most common token.
-
-The probability of a given token is its frequency (the number of times we find it) in the original corpus, divided by the sum of all frequencies of all tokens in the vocabulary (to make sure the probabilities sum up to 1). For instance, `"ug"` is present in `"hug"`, `"pug"`, and `"hugs"`, so it has a frequency of 20 in our corpus.
-
-Here are the frequencies of all the possible subwords in the vocabulary:
-
-```
-("h", 15) ("u", 36) ("g", 20) ("hu", 15) ("ug", 20) ("p", 17) ("pu", 17) ("n", 16)
-("un", 16) ("b", 4) ("bu", 4) ("s", 5) ("hug", 15) ("gs", 5) ("ugs", 5)
-```
-
-So, the sum of all frequencies is 210, and the probability of the subword `"ug"` is thus 20/210.
-
-> [!TIP]
-> ✏️ **Now your turn!** Write the code to compute the frequencies above and double-check that the results shown are correct, as well as the total sum.
-
-Now, to tokenize a given word, we look at all the possible segmentations into tokens and compute the probability of each according to the Unigram model. Since all tokens are considered independent, this probability is just the product of the probability of each token. For instance, the tokenization `["p", "u", "g"]` of `"pug"` has the probability:
-
-$$P([``p", ``u", ``g"]) = P(``p") \times P(``u") \times P(``g") = \frac{5}{210} \times \frac{36}{210} \times \frac{20}{210} = 0.000389$$
-
-Comparatively, the tokenization `["pu", "g"]` has the probability:
-
-$$P([``pu", ``g"]) = P(``pu") \times P(``g") = \frac{5}{210} \times \frac{20}{210} = 0.0022676$$
-
-so that one is way more likely. In general, tokenizations with the least tokens possible will have the highest probability (because of that division by 210 repeated for each token), which corresponds to what we want intuitively: to split a word into the least number of tokens possible.
-
-The tokenization of a word with the Unigram model is then the tokenization with the highest probability. In the example of `"pug"`, here are the probabilities we would get for each possible segmentation:
-
-```
-["p", "u", "g"] : 0.000389
-["p", "ug"] : 0.0022676
-["pu", "g"] : 0.0022676
-```
-
-So, `"pug"` would be tokenized as `["p", "ug"]` or `["pu", "g"]`, depending on which of those segmentations is encountered first (note that in a larger corpus, equality cases like this will be rare).
-
-In this case, it was easy to find all the possible segmentations and compute their probabilities, but in general it's going to be a bit harder. There is a classic algorithm used for this, called the *Viterbi algorithm*. Essentially, we can build a graph to detect the possible segmentations of a given word by saying there is a branch from character _a_ to character _b_ if the subword from _a_ to _b_ is in the vocabulary, and attribute to that branch the probability of the subword.
-
-To find the path in that graph that is going to have the best score the Viterbi algorithm determines, for each position in the word, the segmentation with the best score that ends at that position. Since we go from the beginning to the end, that best score can be found by looping through all subwords ending at the current position and then using the best tokenization score from the position this subword begins at. Then, we just have to unroll the path taken to arrive at the end.
-
-Let's take a look at an example using our vocabulary and the word `"unhug"`. For each position, the subwords with the best scores ending there are the following:
-
-```
-Character 0 (u): "u" (score 0.171429)
-Character 1 (n): "un" (score 0.076191)
-Character 2 (h): "un" "h" (score 0.005442)
-Character 3 (u): "un" "hu" (score 0.005442)
-Character 4 (g): "un" "hug" (score 0.005442)
-```
-
-Thus `"unhug"` would be tokenized as `["un", "hug"]`.
-
-> [!TIP]
-> ✏️ **Now your turn!** Determine the tokenization of the word `"huggun"`, and its score.
-
-## Back to training[[back-to-training]]
-
-Now that we have seen how the tokenization works, we can dive a little more deeply into the loss used during training. At any given stage, this loss is computed by tokenizing every word in the corpus, using the current vocabulary and the Unigram model determined by the frequencies of each token in the corpus (as seen before).
-
-Each word in the corpus has a score, and the loss is the negative log likelihood of those scores -- that is, the sum for all the words in the corpus of all the `-log(P(word))`.
-
-Let's go back to our example with the following corpus:
-
-```
-("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
-```
-
-The tokenization of each word with their respective scores is:
-
-```
-"hug": ["hug"] (score 0.071428)
-"pug": ["pu", "g"] (score 0.007710)
-"pun": ["pu", "n"] (score 0.006168)
-"bun": ["bu", "n"] (score 0.001451)
-"hugs": ["hug", "s"] (score 0.001701)
-```
-
-So the loss is:
-
-```
-10 * (-log(0.071428)) + 5 * (-log(0.007710)) + 12 * (-log(0.006168)) + 4 * (-log(0.001451)) + 5 * (-log(0.001701)) = 169.8
-```
-
-Now we need to compute how removing each token affects the loss. This is rather tedious, so we'll just do it for two tokens here and save the whole process for when we have code to help us. In this (very) particular case, we had two equivalent tokenizations of all the words: as we saw earlier, for example, `"pug"` could be tokenized `["p", "ug"]` with the same score. Thus, removing the `"pu"` token from the vocabulary will give the exact same loss.
-
-On the other hand, removing `"hug"` will make the loss worse, because the tokenization of `"hug"` and `"hugs"` will become:
-
-```
-"hug": ["hu", "g"] (score 0.006802)
-"hugs": ["hu", "gs"] (score 0.001701)
-```
-
-These changes will cause the loss to rise by:
-
-```
-- 10 * (-log(0.071428)) + 10 * (-log(0.006802)) = 23.5
-```
-
-Therefore, the token `"pu"` will probably be removed from the vocabulary, but not `"hug"`.
-
-## Implementing Unigram[[implementing-unigram]]
-
-Now let's implement everything we've seen so far in code. Like with BPE and WordPiece, this is not an efficient implementation of the Unigram algorithm (quite the opposite), but it should help you understand it a bit better.
-
-We will use the same corpus as before as an example:
-
-```python
-corpus = [
- "This is the Hugging Face Course.",
- "This chapter is about tokenization.",
- "This section shows several tokenizer algorithms.",
- "Hopefully, you will be able to understand how they are trained and generate tokens.",
-]
-```
-
-This time, we will use `xlnet-base-cased` as our model:
-
-```python
-from transformers import AutoTokenizer
-
-tokenizer = AutoTokenizer.from_pretrained("xlnet-base-cased")
-```
-
-Like for BPE and WordPiece, we begin by counting the number of occurrences of each word in the corpus:
-
-```python
-from collections import defaultdict
-
-word_freqs = defaultdict(int)
-for text in corpus:
- words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
- new_words = [word for word, offset in words_with_offsets]
- for word in new_words:
- word_freqs[word] += 1
-
-word_freqs
-```
-
-Then, we need to initialize our vocabulary to something larger than the vocab size we will want at the end. We have to include all the basic characters (otherwise we won't be able to tokenize every word), but for the bigger substrings we'll only keep the most common ones, so we sort them by frequency:
-
-```python
-char_freqs = defaultdict(int)
-subwords_freqs = defaultdict(int)
-for word, freq in word_freqs.items():
- for i in range(len(word)):
- char_freqs[word[i]] += freq
- # Loop through the subwords of length at least 2
- for j in range(i + 2, len(word) + 1):
- subwords_freqs[word[i:j]] += freq
-
-# Sort subwords by frequency
-sorted_subwords = sorted(subwords_freqs.items(), key=lambda x: x[1], reverse=True)
-sorted_subwords[:10]
-```
-
-```python out
-[('▁t', 7), ('is', 5), ('er', 5), ('▁a', 5), ('▁to', 4), ('to', 4), ('en', 4), ('▁T', 3), ('▁Th', 3), ('▁Thi', 3)]
-```
-
-We group the characters with the best subwords to arrive at an initial vocabulary of size 300:
-
-```python
-token_freqs = list(char_freqs.items()) + sorted_subwords[: 300 - len(char_freqs)]
-token_freqs = {token: freq for token, freq in token_freqs}
-```
-
-> [!TIP]
-> 💡 SentencePiece uses a more efficient algorithm called Enhanced Suffix Array (ESA) to create the initial vocabulary.
-
-Next, we compute the sum of all frequencies, to convert the frequencies into probabilities. For our model we will store the logarithms of the probabilities, because it's more numerically stable to add logarithms than to multiply small numbers, and this will simplify the computation of the loss of the model:
-
-```python
-from math import log
-
-total_sum = sum([freq for token, freq in token_freqs.items()])
-model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
-```
-
-Now the main function is the one that tokenizes words using the Viterbi algorithm. As we saw before, that algorithm computes the best segmentation of each substring of the word, which we will store in a variable named `best_segmentations`. We will store one dictionary per position in the word (from 0 to its total length), with two keys: the index of the start of the last token in the best segmentation, and the score of the best segmentation. With the index of the start of the last token, we will be able to retrieve the full segmentation once the list is completely populated.
-
-Populating the list is done with just two loops: the main loop goes over each start position, and the second loop tries all substrings beginning at that start position. If the substring is in the vocabulary, we have a new segmentation of the word up until that end position, which we compare to what is in `best_segmentations`.
-
-Once the main loop is finished, we just start from the end and hop from one start position to the next, recording the tokens as we go, until we reach the start of the word:
-
-```python
-def encode_word(word, model):
- best_segmentations = [{"start": 0, "score": 1}] + [
- {"start": None, "score": None} for _ in range(len(word))
- ]
- for start_idx in range(len(word)):
- # This should be properly filled by the previous steps of the loop
- best_score_at_start = best_segmentations[start_idx]["score"]
- for end_idx in range(start_idx + 1, len(word) + 1):
- token = word[start_idx:end_idx]
- if token in model and best_score_at_start is not None:
- score = model[token] + best_score_at_start
- # If we have found a better segmentation ending at end_idx, we update
- if (
- best_segmentations[end_idx]["score"] is None
- or best_segmentations[end_idx]["score"] > score
- ):
- best_segmentations[end_idx] = {"start": start_idx, "score": score}
-
- segmentation = best_segmentations[-1]
- if segmentation["score"] is None:
- # We did not find a tokenization of the word -> unknown
- return [""], None
-
- score = segmentation["score"]
- start = segmentation["start"]
- end = len(word)
- tokens = []
- while start != 0:
- tokens.insert(0, word[start:end])
- next_start = best_segmentations[start]["start"]
- end = start
- start = next_start
- tokens.insert(0, word[start:end])
- return tokens, score
-```
-
-We can already try our initial model on some words:
-
-```python
-print(encode_word("Hopefully", model))
-print(encode_word("This", model))
-```
-
-```python out
-(['H', 'o', 'p', 'e', 'f', 'u', 'll', 'y'], 41.5157494601402)
-(['This'], 6.288267030694535)
-```
-
-Now it's easy to compute the loss of the model on the corpus!
-
-```python
-def compute_loss(model):
- loss = 0
- for word, freq in word_freqs.items():
- _, word_loss = encode_word(word, model)
- loss += freq * word_loss
- return loss
-```
-
-We can check it works on the model we have:
-
-```python
-compute_loss(model)
-```
-
-```python out
-413.10377642940875
-```
-
-Computing the scores for each token is not very hard either; we just have to compute the loss for the models obtained by deleting each token:
-
-```python
-import copy
-
-
-def compute_scores(model):
- scores = {}
- model_loss = compute_loss(model)
- for token, score in model.items():
- # We always keep tokens of length 1
- if len(token) == 1:
- continue
- model_without_token = copy.deepcopy(model)
- _ = model_without_token.pop(token)
- scores[token] = compute_loss(model_without_token) - model_loss
- return scores
-```
-
-We can try it on a given token:
-
-```python
-scores = compute_scores(model)
-print(scores["ll"])
-print(scores["his"])
-```
-
-Since `"ll"` is used in the tokenization of `"Hopefully"`, and removing it will probably make us use the token `"l"` twice instead, we expect it will have a positive loss. `"his"` is only used inside the word `"This"`, which is tokenized as itself, so we expect it to have a zero loss. Here are the results:
-
-```python out
-6.376412403623874
-0.0
-```
-
-> [!TIP]
-> 💡 This approach is very inefficient, so SentencePiece uses an approximation of the loss of the model without token X: instead of starting from scratch, it just replaces token X by its segmentation in the vocabulary that is left. This way, all the scores can be computed at once at the same time as the model loss.
-
-With all of this in place, the last thing we need to do is add the special tokens used by the model to the vocabulary, then loop until we have pruned enough tokens from the vocabulary to reach our desired size:
-
-```python
-percent_to_remove = 0.1
-while len(model) > 100:
- scores = compute_scores(model)
- sorted_scores = sorted(scores.items(), key=lambda x: x[1])
- # Remove percent_to_remove tokens with the lowest scores.
- for i in range(int(len(model) * percent_to_remove)):
- _ = token_freqs.pop(sorted_scores[i][0])
-
- total_sum = sum([freq for token, freq in token_freqs.items()])
- model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
-```
-
-Then, to tokenize some text, we just need to apply the pre-tokenization and then use our `encode_word()` function:
-
-```python
-def tokenize(text, model):
- words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
- pre_tokenized_text = [word for word, offset in words_with_offsets]
- encoded_words = [encode_word(word, model)[0] for word in pre_tokenized_text]
- return sum(encoded_words, [])
-
-
-tokenize("This is the Hugging Face course.", model)
-```
-
-```python out
-['▁This', '▁is', '▁the', '▁Hugging', '▁Face', '▁', 'c', 'ou', 'r', 's', 'e', '.']
-```
-
-> [!TIP]
-> The XLNetTokenizer uses SentencePiece which is why the `"_"` character is included. To decode with SentencePiece, concatenate all the tokens and replace `"_"` with a space.
-
-That's it for Unigram! Hopefully by now you're feeling like an expert in all things tokenizer. In the next section, we will delve into the building blocks of the 🤗 Tokenizers library, and show you how you can use them to build your own tokenizer.
+# Unigram tokenization[[unigram-tokenization]]
+
+
+
+The Unigram algorithm is used in combination with [SentencePiece](https://huggingface.co/papers/1808.06226), which is the tokenization algorithm used by models like AlBERT, T5, mBART, Big Bird, and XLNet.
+
+SentencePiece addresses the fact that not all languages use spaces to separate words. Instead, SentencePiece treats the input as a raw input stream which includes the space in the set of characters to use. Then it can use the Unigram algorithm to construct the appropriate vocabulary.
+
+
+
+> [!TIP]
+> 💡 This section covers Unigram in depth, going as far as showing a full implementation. You can skip to the end if you just want a general overview of the tokenization algorithm.
+
+## Training algorithm[[training-algorithm]]
+
+Compared to BPE and WordPiece, Unigram works in the other direction: it starts from a big vocabulary and removes tokens from it until it reaches the desired vocabulary size. There are several options to use to build that base vocabulary: we can take the most common substrings in pre-tokenized words, for instance, or apply BPE on the initial corpus with a large vocabulary size.
+
+At each step of the training, the Unigram algorithm computes a loss over the corpus given the current vocabulary. Then, for each symbol in the vocabulary, the algorithm computes how much the overall loss would increase if the symbol was removed, and looks for the symbols that would increase it the least. Those symbols have a lower effect on the overall loss over the corpus, so in a sense they are "less needed" and are the best candidates for removal.
+
+This is all a very costly operation, so we don't just remove the single symbol associated with the lowest loss increase, but the \\(p\\) (\\(p\\) being a hyperparameter you can control, usually 10 or 20) percent of the symbols associated with the lowest loss increase. This process is then repeated until the vocabulary has reached the desired size.
+
+Note that we never remove the base characters, to make sure any word can be tokenized.
+
+Now, this is still a bit vague: the main part of the algorithm is to compute a loss over the corpus and see how it changes when we remove some tokens from the vocabulary, but we haven't explained how to do this yet. This step relies on the tokenization algorithm of a Unigram model, so we'll dive into this next.
+
+We'll reuse the corpus from the previous examples:
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+and for this example, we will take all strict substrings for the initial vocabulary :
+
+```
+["h", "u", "g", "hu", "ug", "p", "pu", "n", "un", "b", "bu", "s", "hug", "gs", "ugs"]
+```
+
+## Tokenization algorithm[[tokenization-algorithm]]
+
+A Unigram model is a type of language model that considers each token to be independent of the tokens before it. It's the simplest language model, in the sense that the probability of token X given the previous context is just the probability of token X. So, if we used a Unigram language model to generate text, we would always predict the most common token.
+
+The probability of a given token is its frequency (the number of times we find it) in the original corpus, divided by the sum of all frequencies of all tokens in the vocabulary (to make sure the probabilities sum up to 1). For instance, `"ug"` is present in `"hug"`, `"pug"`, and `"hugs"`, so it has a frequency of 20 in our corpus.
+
+Here are the frequencies of all the possible subwords in the vocabulary:
+
+```
+("h", 15) ("u", 36) ("g", 20) ("hu", 15) ("ug", 20) ("p", 17) ("pu", 17) ("n", 16)
+("un", 16) ("b", 4) ("bu", 4) ("s", 5) ("hug", 15) ("gs", 5) ("ugs", 5)
+```
+
+So, the sum of all frequencies is 210, and the probability of the subword `"ug"` is thus 20/210.
+
+> [!TIP]
+> ✏️ **Now your turn!** Write the code to compute the frequencies above and double-check that the results shown are correct, as well as the total sum.
+
+Now, to tokenize a given word, we look at all the possible segmentations into tokens and compute the probability of each according to the Unigram model. Since all tokens are considered independent, this probability is just the product of the probability of each token. For instance, the tokenization `["p", "u", "g"]` of `"pug"` has the probability:
+
+$$P([``p", ``u", ``g"]) = P(``p") \times P(``u") \times P(``g") = \frac{5}{210} \times \frac{36}{210} \times \frac{20}{210} = 0.000389$$
+
+Comparatively, the tokenization `["pu", "g"]` has the probability:
+
+$$P([``pu", ``g"]) = P(``pu") \times P(``g") = \frac{5}{210} \times \frac{20}{210} = 0.0022676$$
+
+so that one is way more likely. In general, tokenizations with the least tokens possible will have the highest probability (because of that division by 210 repeated for each token), which corresponds to what we want intuitively: to split a word into the least number of tokens possible.
+
+The tokenization of a word with the Unigram model is then the tokenization with the highest probability. In the example of `"pug"`, here are the probabilities we would get for each possible segmentation:
+
+```
+["p", "u", "g"] : 0.000389
+["p", "ug"] : 0.0022676
+["pu", "g"] : 0.0022676
+```
+
+So, `"pug"` would be tokenized as `["p", "ug"]` or `["pu", "g"]`, depending on which of those segmentations is encountered first (note that in a larger corpus, equality cases like this will be rare).
+
+In this case, it was easy to find all the possible segmentations and compute their probabilities, but in general it's going to be a bit harder. There is a classic algorithm used for this, called the *Viterbi algorithm*. Essentially, we can build a graph to detect the possible segmentations of a given word by saying there is a branch from character _a_ to character _b_ if the subword from _a_ to _b_ is in the vocabulary, and attribute to that branch the probability of the subword.
+
+To find the path in that graph that is going to have the best score the Viterbi algorithm determines, for each position in the word, the segmentation with the best score that ends at that position. Since we go from the beginning to the end, that best score can be found by looping through all subwords ending at the current position and then using the best tokenization score from the position this subword begins at. Then, we just have to unroll the path taken to arrive at the end.
+
+Let's take a look at an example using our vocabulary and the word `"unhug"`. For each position, the subwords with the best scores ending there are the following:
+
+```
+Character 0 (u): "u" (score 0.171429)
+Character 1 (n): "un" (score 0.076191)
+Character 2 (h): "un" "h" (score 0.005442)
+Character 3 (u): "un" "hu" (score 0.005442)
+Character 4 (g): "un" "hug" (score 0.005442)
+```
+
+Thus `"unhug"` would be tokenized as `["un", "hug"]`.
+
+> [!TIP]
+> ✏️ **Now your turn!** Determine the tokenization of the word `"huggun"`, and its score.
+
+## Back to training[[back-to-training]]
+
+Now that we have seen how the tokenization works, we can dive a little more deeply into the loss used during training. At any given stage, this loss is computed by tokenizing every word in the corpus, using the current vocabulary and the Unigram model determined by the frequencies of each token in the corpus (as seen before).
+
+Each word in the corpus has a score, and the loss is the negative log likelihood of those scores -- that is, the sum for all the words in the corpus of all the `-log(P(word))`.
+
+Let's go back to our example with the following corpus:
+
+```
+("hug", 10), ("pug", 5), ("pun", 12), ("bun", 4), ("hugs", 5)
+```
+
+The tokenization of each word with their respective scores is:
+
+```
+"hug": ["hug"] (score 0.071428)
+"pug": ["pu", "g"] (score 0.007710)
+"pun": ["pu", "n"] (score 0.006168)
+"bun": ["bu", "n"] (score 0.001451)
+"hugs": ["hug", "s"] (score 0.001701)
+```
+
+So the loss is:
+
+```
+10 * (-log(0.071428)) + 5 * (-log(0.007710)) + 12 * (-log(0.006168)) + 4 * (-log(0.001451)) + 5 * (-log(0.001701)) = 169.8
+```
+
+Now we need to compute how removing each token affects the loss. This is rather tedious, so we'll just do it for two tokens here and save the whole process for when we have code to help us. In this (very) particular case, we had two equivalent tokenizations of all the words: as we saw earlier, for example, `"pug"` could be tokenized `["p", "ug"]` with the same score. Thus, removing the `"pu"` token from the vocabulary will give the exact same loss.
+
+On the other hand, removing `"hug"` will make the loss worse, because the tokenization of `"hug"` and `"hugs"` will become:
+
+```
+"hug": ["hu", "g"] (score 0.006802)
+"hugs": ["hu", "gs"] (score 0.001701)
+```
+
+These changes will cause the loss to rise by:
+
+```
+- 10 * (-log(0.071428)) + 10 * (-log(0.006802)) = 23.5
+```
+
+Therefore, the token `"pu"` will probably be removed from the vocabulary, but not `"hug"`.
+
+## Implementing Unigram[[implementing-unigram]]
+
+Now let's implement everything we've seen so far in code. Like with BPE and WordPiece, this is not an efficient implementation of the Unigram algorithm (quite the opposite), but it should help you understand it a bit better.
+
+We will use the same corpus as before as an example:
+
+```python
+corpus = [
+ "This is the Hugging Face Course.",
+ "This chapter is about tokenization.",
+ "This section shows several tokenizer algorithms.",
+ "Hopefully, you will be able to understand how they are trained and generate tokens.",
+]
+```
+
+This time, we will use `xlnet-base-cased` as our model:
+
+```python
+from transformers import AutoTokenizer
+
+tokenizer = AutoTokenizer.from_pretrained("xlnet-base-cased")
+```
+
+Like for BPE and WordPiece, we begin by counting the number of occurrences of each word in the corpus:
+
+```python
+from collections import defaultdict
+
+word_freqs = defaultdict(int)
+for text in corpus:
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ new_words = [word for word, offset in words_with_offsets]
+ for word in new_words:
+ word_freqs[word] += 1
+
+word_freqs
+```
+
+Then, we need to initialize our vocabulary to something larger than the vocab size we will want at the end. We have to include all the basic characters (otherwise we won't be able to tokenize every word), but for the bigger substrings we'll only keep the most common ones, so we sort them by frequency:
+
+```python
+char_freqs = defaultdict(int)
+subwords_freqs = defaultdict(int)
+for word, freq in word_freqs.items():
+ for i in range(len(word)):
+ char_freqs[word[i]] += freq
+ # Loop through the subwords of length at least 2
+ for j in range(i + 2, len(word) + 1):
+ subwords_freqs[word[i:j]] += freq
+
+# Sort subwords by frequency
+sorted_subwords = sorted(subwords_freqs.items(), key=lambda x: x[1], reverse=True)
+sorted_subwords[:10]
+```
+
+```python out
+[('▁t', 7), ('is', 5), ('er', 5), ('▁a', 5), ('▁to', 4), ('to', 4), ('en', 4), ('▁T', 3), ('▁Th', 3), ('▁Thi', 3)]
+```
+
+We group the characters with the best subwords to arrive at an initial vocabulary of size 300:
+
+```python
+token_freqs = list(char_freqs.items()) + sorted_subwords[: 300 - len(char_freqs)]
+token_freqs = {token: freq for token, freq in token_freqs}
+```
+
+> [!TIP]
+> 💡 SentencePiece uses a more efficient algorithm called Enhanced Suffix Array (ESA) to create the initial vocabulary.
+
+Next, we compute the sum of all frequencies, to convert the frequencies into probabilities. For our model we will store the logarithms of the probabilities, because it's more numerically stable to add logarithms than to multiply small numbers, and this will simplify the computation of the loss of the model:
+
+```python
+from math import log
+
+total_sum = sum([freq for token, freq in token_freqs.items()])
+model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
+```
+
+Now the main function is the one that tokenizes words using the Viterbi algorithm. As we saw before, that algorithm computes the best segmentation of each substring of the word, which we will store in a variable named `best_segmentations`. We will store one dictionary per position in the word (from 0 to its total length), with two keys: the index of the start of the last token in the best segmentation, and the score of the best segmentation. With the index of the start of the last token, we will be able to retrieve the full segmentation once the list is completely populated.
+
+Populating the list is done with just two loops: the main loop goes over each start position, and the second loop tries all substrings beginning at that start position. If the substring is in the vocabulary, we have a new segmentation of the word up until that end position, which we compare to what is in `best_segmentations`.
+
+Once the main loop is finished, we just start from the end and hop from one start position to the next, recording the tokens as we go, until we reach the start of the word:
+
+```python
+def encode_word(word, model):
+ best_segmentations = [{"start": 0, "score": 1}] + [
+ {"start": None, "score": None} for _ in range(len(word))
+ ]
+ for start_idx in range(len(word)):
+ # This should be properly filled by the previous steps of the loop
+ best_score_at_start = best_segmentations[start_idx]["score"]
+ for end_idx in range(start_idx + 1, len(word) + 1):
+ token = word[start_idx:end_idx]
+ if token in model and best_score_at_start is not None:
+ score = model[token] + best_score_at_start
+ # If we have found a better segmentation ending at end_idx, we update
+ if (
+ best_segmentations[end_idx]["score"] is None
+ or best_segmentations[end_idx]["score"] > score
+ ):
+ best_segmentations[end_idx] = {"start": start_idx, "score": score}
+
+ segmentation = best_segmentations[-1]
+ if segmentation["score"] is None:
+ # We did not find a tokenization of the word -> unknown
+ return [""], None
+
+ score = segmentation["score"]
+ start = segmentation["start"]
+ end = len(word)
+ tokens = []
+ while start != 0:
+ tokens.insert(0, word[start:end])
+ next_start = best_segmentations[start]["start"]
+ end = start
+ start = next_start
+ tokens.insert(0, word[start:end])
+ return tokens, score
+```
+
+We can already try our initial model on some words:
+
+```python
+print(encode_word("Hopefully", model))
+print(encode_word("This", model))
+```
+
+```python out
+(['H', 'o', 'p', 'e', 'f', 'u', 'll', 'y'], 41.5157494601402)
+(['This'], 6.288267030694535)
+```
+
+Now it's easy to compute the loss of the model on the corpus!
+
+```python
+def compute_loss(model):
+ loss = 0
+ for word, freq in word_freqs.items():
+ _, word_loss = encode_word(word, model)
+ loss += freq * word_loss
+ return loss
+```
+
+We can check it works on the model we have:
+
+```python
+compute_loss(model)
+```
+
+```python out
+413.10377642940875
+```
+
+Computing the scores for each token is not very hard either; we just have to compute the loss for the models obtained by deleting each token:
+
+```python
+import copy
+
+
+def compute_scores(model):
+ scores = {}
+ model_loss = compute_loss(model)
+ for token, score in model.items():
+ # We always keep tokens of length 1
+ if len(token) == 1:
+ continue
+ model_without_token = copy.deepcopy(model)
+ _ = model_without_token.pop(token)
+ scores[token] = compute_loss(model_without_token) - model_loss
+ return scores
+```
+
+We can try it on a given token:
+
+```python
+scores = compute_scores(model)
+print(scores["ll"])
+print(scores["his"])
+```
+
+Since `"ll"` is used in the tokenization of `"Hopefully"`, and removing it will probably make us use the token `"l"` twice instead, we expect it will have a positive loss. `"his"` is only used inside the word `"This"`, which is tokenized as itself, so we expect it to have a zero loss. Here are the results:
+
+```python out
+6.376412403623874
+0.0
+```
+
+> [!TIP]
+> 💡 This approach is very inefficient, so SentencePiece uses an approximation of the loss of the model without token X: instead of starting from scratch, it just replaces token X by its segmentation in the vocabulary that is left. This way, all the scores can be computed at once at the same time as the model loss.
+
+With all of this in place, the last thing we need to do is add the special tokens used by the model to the vocabulary, then loop until we have pruned enough tokens from the vocabulary to reach our desired size:
+
+```python
+percent_to_remove = 0.1
+while len(model) > 100:
+ scores = compute_scores(model)
+ sorted_scores = sorted(scores.items(), key=lambda x: x[1])
+ # Remove percent_to_remove tokens with the lowest scores.
+ for i in range(int(len(model) * percent_to_remove)):
+ _ = token_freqs.pop(sorted_scores[i][0])
+
+ total_sum = sum([freq for token, freq in token_freqs.items()])
+ model = {token: -log(freq / total_sum) for token, freq in token_freqs.items()}
+```
+
+Then, to tokenize some text, we just need to apply the pre-tokenization and then use our `encode_word()` function:
+
+```python
+def tokenize(text, model):
+ words_with_offsets = tokenizer.backend_tokenizer.pre_tokenizer.pre_tokenize_str(text)
+ pre_tokenized_text = [word for word, offset in words_with_offsets]
+ encoded_words = [encode_word(word, model)[0] for word in pre_tokenized_text]
+ return sum(encoded_words, [])
+
+
+tokenize("This is the Hugging Face course.", model)
+```
+
+```python out
+['▁This', '▁is', '▁the', '▁Hugging', '▁Face', '▁', 'c', 'ou', 'r', 's', 'e', '.']
+```
+
+> [!TIP]
+> The XLNetTokenizer uses SentencePiece which is why the `"_"` character is included. To decode with SentencePiece, concatenate all the tokens and replace `"_"` with a space.
+
+That's it for Unigram! Hopefully by now you're feeling like an expert in all things tokenizer. In the next section, we will delve into the building blocks of the 🤗 Tokenizers library, and show you how you can use them to build your own tokenizer.
diff --git a/chapters/en/chapter6/8.mdx b/chapters/en/chapter6/8.mdx
index 71bcafbe2..34dd2c15f 100644
--- a/chapters/en/chapter6/8.mdx
+++ b/chapters/en/chapter6/8.mdx
@@ -1,562 +1,562 @@
-# Building a tokenizer, block by block[[building-a-tokenizer-block-by-block]]
-
-
-
-As we've seen in the previous sections, tokenization comprises several steps:
-
-- Normalization (any cleanup of the text that is deemed necessary, such as removing spaces or accents, Unicode normalization, etc.)
-- Pre-tokenization (splitting the input into words)
-- Running the input through the model (using the pre-tokenized words to produce a sequence of tokens)
-- Post-processing (adding the special tokens of the tokenizer, generating the attention mask and token type IDs)
-
-As a reminder, here's another look at the overall process:
-
-
-

-

-
-
-The 🤗 Tokenizers library has been built to provide several options for each of those steps, which you can mix and match together. In this section we'll see how we can build a tokenizer from scratch, as opposed to training a new tokenizer from an old one as we did in [section 2](/course/chapter6/2). You'll then be able to build any kind of tokenizer you can think of!
-
-
-
-More precisely, the library is built around a central `Tokenizer` class with the building blocks regrouped in submodules:
-
-- `normalizers` contains all the possible types of `Normalizer` you can use (complete list [here](https://huggingface.co/docs/tokenizers/api/normalizers)).
-- `pre_tokenizers` contains all the possible types of `PreTokenizer` you can use (complete list [here](https://huggingface.co/docs/tokenizers/api/pre-tokenizers)).
-- `models` contains the various types of `Model` you can use, like `BPE`, `WordPiece`, and `Unigram` (complete list [here](https://huggingface.co/docs/tokenizers/api/models)).
-- `trainers` contains all the different types of `Trainer` you can use to train your model on a corpus (one per type of model; complete list [here](https://huggingface.co/docs/tokenizers/api/trainers)).
-- `post_processors` contains the various types of `PostProcessor` you can use (complete list [here](https://huggingface.co/docs/tokenizers/api/post-processors)).
-- `decoders` contains the various types of `Decoder` you can use to decode the outputs of tokenization (complete list [here](https://huggingface.co/docs/tokenizers/components#decoders)).
-
-You can find the whole list of building blocks [here](https://huggingface.co/docs/tokenizers/components).
-
-## Acquiring a corpus[[acquiring-a-corpus]]
-
-To train our new tokenizer, we will use a small corpus of text (so the examples run fast). The steps for acquiring the corpus are similar to the ones we took at the [beginning of this chapter](/course/chapter6/2), but this time we'll use the [WikiText-2](https://huggingface.co/datasets/wikitext) dataset:
-
-```python
-from datasets import load_dataset
-
-dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train")
-
-
-def get_training_corpus():
- for i in range(0, len(dataset), 1000):
- yield dataset[i : i + 1000]["text"]
-```
-
-The function `get_training_corpus()` is a generator that will yield batches of 1,000 texts, which we will use to train the tokenizer.
-
-🤗 Tokenizers can also be trained on text files directly. Here's how we can generate a text file containing all the texts/inputs from WikiText-2 that we can use locally:
-
-```python
-with open("wikitext-2.txt", "w", encoding="utf-8") as f:
- for i in range(len(dataset)):
- f.write(dataset[i]["text"] + "\n")
-```
-
-Next we'll show you how to build your own BERT, GPT-2, and XLNet tokenizers, block by block. That will give us an example of each of the three main tokenization algorithms: WordPiece, BPE, and Unigram. Let's start with BERT!
-
-## Building a WordPiece tokenizer from scratch[[building-a-wordpiece-tokenizer-from-scratch]]
-
-To build a tokenizer with the 🤗 Tokenizers library, we start by instantiating a `Tokenizer` object with a `model`, then set its `normalizer`, `pre_tokenizer`, `post_processor`, and `decoder` attributes to the values we want.
-
-For this example, we'll create a `Tokenizer` with a WordPiece model:
-
-```python
-from tokenizers import (
- decoders,
- models,
- normalizers,
- pre_tokenizers,
- processors,
- trainers,
- Tokenizer,
-)
-
-tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))
-```
-
-We have to specify the `unk_token` so the model knows what to return when it encounters characters it hasn't seen before. Other arguments we can set here include the `vocab` of our model (we're going to train the model, so we don't need to set this) and `max_input_chars_per_word`, which specifies a maximum length for each word (words longer than the value passed will be split).
-
-The first step of tokenization is normalization, so let's begin with that. Since BERT is widely used, there is a `BertNormalizer` with the classic options we can set for BERT: `lowercase` and `strip_accents`, which are self-explanatory; `clean_text` to remove all control characters and replace repeating spaces with a single one; and `handle_chinese_chars`, which places spaces around Chinese characters. To replicate the `bert-base-uncased` tokenizer, we can just set this normalizer:
-
-```python
-tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True)
-```
-
-Generally speaking, however, when building a new tokenizer you won't have access to such a handy normalizer already implemented in the 🤗 Tokenizers library -- so let's see how to create the BERT normalizer by hand. The library provides a `Lowercase` normalizer and a `StripAccents` normalizer, and you can compose several normalizers using a `Sequence`:
-
-```python
-tokenizer.normalizer = normalizers.Sequence(
- [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()]
-)
-```
-
-We're also using an `NFD` Unicode normalizer, as otherwise the `StripAccents` normalizer won't properly recognize the accented characters and thus won't strip them out.
-
-As we've seen before, we can use the `normalize_str()` method of the `normalizer` to check out the effects it has on a given text:
-
-```python
-print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
-```
-
-```python out
-hello how are u?
-```
-
-> [!TIP]
-> **To go further** If you test the two versions of the previous normalizers on a string containing the unicode character `u"\u0085"` you will surely notice that these two normalizers are not exactly equivalent.
-> To not over-complicate the version with `normalizers.Sequence` too much , we haven't included the Regex replacements that the `BertNormalizer` requires when the `clean_text` argument is set to `True` - which is the default behavior. But don't worry: it is possible to get exactly the same normalization without using the handy `BertNormalizer` by adding two `normalizers.Replace`'s to the normalizers sequence.
-
-Next is the pre-tokenization step. Again, there is a prebuilt `BertPreTokenizer` that we can use:
-
-```python
-tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer()
-```
-
-Or we can build it from scratch:
-
-```python
-tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
-```
-
-Note that the `Whitespace` pre-tokenizer splits on whitespace and all characters that are not letters, digits, or the underscore character, so it technically splits on whitespace and punctuation:
-
-```python
-tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
-```
-
-```python out
-[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
- ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
-```
-
-If you only want to split on whitespace, you should use the `WhitespaceSplit` pre-tokenizer instead:
-
-```python
-pre_tokenizer = pre_tokenizers.WhitespaceSplit()
-pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
-```
-
-```python out
-[("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))]
-```
-
-Like with normalizers, you can use a `Sequence` to compose several pre-tokenizers:
-
-```python
-pre_tokenizer = pre_tokenizers.Sequence(
- [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()]
-)
-pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
-```
-
-```python out
-[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
- ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
-```
-
-The next step in the tokenization pipeline is running the inputs through the model. We already specified our model in the initialization, but we still need to train it, which will require a `WordPieceTrainer`. The main thing to remember when instantiating a trainer in 🤗 Tokenizers is that you need to pass it all the special tokens you intend to use -- otherwise it won't add them to the vocabulary, since they are not in the training corpus:
-
-```python
-special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"]
-trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens)
-```
-
-As well as specifying the `vocab_size` and `special_tokens`, we can set the `min_frequency` (the number of times a token must appear to be included in the vocabulary) or change the `continuing_subword_prefix` (if we want to use something different from `##`).
-
-To train our model using the iterator we defined earlier, we just have to execute this command:
-
-```python
-tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
-```
-
-We can also use text files to train our tokenizer, which would look like this (we reinitialize the model with an empty `WordPiece` beforehand):
-
-```python
-tokenizer.model = models.WordPiece(unk_token="[UNK]")
-tokenizer.train(["wikitext-2.txt"], trainer=trainer)
-```
-
-In both cases, we can then test the tokenizer on a text by calling the `encode()` method:
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer.")
-print(encoding.tokens)
-```
-
-```python out
-['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.']
-```
-
-The `encoding` obtained is an `Encoding`, which contains all the necessary outputs of the tokenizer in its various attributes: `ids`, `type_ids`, `tokens`, `offsets`, `attention_mask`, `special_tokens_mask`, and `overflowing`.
-
-The last step in the tokenization pipeline is post-processing. We need to add the `[CLS]` token at the beginning and the `[SEP]` token at the end (or after each sentence, if we have a pair of sentences). We will use a `TemplateProcessor` for this, but first we need to know the IDs of the `[CLS]` and `[SEP]` tokens in the vocabulary:
-
-```python
-cls_token_id = tokenizer.token_to_id("[CLS]")
-sep_token_id = tokenizer.token_to_id("[SEP]")
-print(cls_token_id, sep_token_id)
-```
-
-```python out
-(2, 3)
-```
-
-To write the template for the `TemplateProcessor`, we have to specify how to treat a single sentence and a pair of sentences. For both, we write the special tokens we want to use; the first (or single) sentence is represented by `$A`, while the second sentence (if encoding a pair) is represented by `$B`. For each of these (special tokens and sentences), we also specify the corresponding token type ID after a colon.
-
-The classic BERT template is thus defined as follows:
-
-```python
-tokenizer.post_processor = processors.TemplateProcessing(
- single=f"[CLS]:0 $A:0 [SEP]:0",
- pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1",
- special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)],
-)
-```
-
-Note that we need to pass along the IDs of the special tokens, so the tokenizer can properly convert them to their IDs.
-
-Once this is added, going back to our previous example will give:
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer.")
-print(encoding.tokens)
-```
-
-```python out
-['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]']
-```
-
-And on a pair of sentences, we get the proper result:
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.")
-print(encoding.tokens)
-print(encoding.type_ids)
-```
-
-```python out
-['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]']
-[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
-```
-
-We've almost finished building this tokenizer from scratch -- the last step is to include a decoder:
-
-```python
-tokenizer.decoder = decoders.WordPiece(prefix="##")
-```
-
-Let's test it on our previous `encoding`:
-
-```python
-tokenizer.decode(encoding.ids)
-```
-
-```python out
-"let's test this tokenizer... on a pair of sentences."
-```
-
-Great! We can save our tokenizer in a single JSON file like this:
-
-```python
-tokenizer.save("tokenizer.json")
-```
-
-We can then reload that file in a `Tokenizer` object with the `from_file()` method:
-
-```python
-new_tokenizer = Tokenizer.from_file("tokenizer.json")
-```
-
-To use this tokenizer in 🤗 Transformers, we have to wrap it in a `PreTrainedTokenizerFast`. We can either use the generic class or, if our tokenizer corresponds to an existing model, use that class (here, `BertTokenizerFast`). If you apply this lesson to build a brand new tokenizer, you will have to use the first option.
-
-To wrap the tokenizer in a `PreTrainedTokenizerFast`, we can either pass the tokenizer we built as a `tokenizer_object` or pass the tokenizer file we saved as `tokenizer_file`. The key thing to remember is that we have to manually set all the special tokens, since that class can't infer from the `tokenizer` object which token is the mask token, the `[CLS]` token, etc.:
-
-```python
-from transformers import PreTrainedTokenizerFast
-
-wrapped_tokenizer = PreTrainedTokenizerFast(
- tokenizer_object=tokenizer,
- # tokenizer_file="tokenizer.json", # You can load from the tokenizer file, alternatively
- unk_token="[UNK]",
- pad_token="[PAD]",
- cls_token="[CLS]",
- sep_token="[SEP]",
- mask_token="[MASK]",
-)
-```
-
-If you are using a specific tokenizer class (like `BertTokenizerFast`), you will only need to specify the special tokens that are different from the default ones (here, none):
-
-```python
-from transformers import BertTokenizerFast
-
-wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer)
-```
-
-You can then use this tokenizer like any other 🤗 Transformers tokenizer. You can save it with the `save_pretrained()` method, or upload it to the Hub with the `push_to_hub()` method.
-
-Now that we've seen how to build a WordPiece tokenizer, let's do the same for a BPE tokenizer. We'll go a bit faster since you know all the steps, and only highlight the differences.
-
-## Building a BPE tokenizer from scratch[[building-a-bpe-tokenizer-from-scratch]]
-
-Let's now build a GPT-2 tokenizer. Like for the BERT tokenizer, we start by initializing a `Tokenizer` with a BPE model:
-
-```python
-tokenizer = Tokenizer(models.BPE())
-```
-
-Also like for BERT, we could initialize this model with a vocabulary if we had one (we would need to pass the `vocab` and `merges` in this case), but since we will train from scratch, we don't need to do that. We also don't need to specify an `unk_token` because GPT-2 uses byte-level BPE, which doesn't require it.
-
-GPT-2 does not use a normalizer, so we skip that step and go directly to the pre-tokenization:
-
-```python
-tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
-```
-
-The option we added to `ByteLevel` here is to not add a space at the beginning of a sentence (which is the default otherwise). We can have a look at the pre-tokenization of an example text like before:
-
-```python
-tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!")
-```
-
-```python out
-[('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)),
- ('tokenization', (15, 27)), ('!', (27, 28))]
-```
-
-Next is the model, which needs training. For GPT-2, the only special token is the end-of-text token:
-
-```python
-trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
-tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
-```
-
-Like with the `WordPieceTrainer`, as well as the `vocab_size` and `special_tokens`, we can specify the `min_frequency` if we want to, or if we have an end-of-word suffix (like ``), we can set it with `end_of_word_suffix`.
-
-This tokenizer can also be trained on text files:
-
-```python
-tokenizer.model = models.BPE()
-tokenizer.train(["wikitext-2.txt"], trainer=trainer)
-```
-
-Let's have a look at the tokenization of a sample text:
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer.")
-print(encoding.tokens)
-```
-
-```python out
-['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']
-```
-
-We apply the byte-level post-processing for the GPT-2 tokenizer as follows:
-
-```python
-tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
-```
-
-The `trim_offsets = False` option indicates to the post-processor that we should leave the offsets of tokens that begin with 'Ġ' as they are: this way the start of the offsets will point to the space before the word, not the first character of the word (since the space is technically part of the token). Let's have a look at the result with the text we just encoded, where `'Ġtest'` is the token at index 4:
-
-```python
-sentence = "Let's test this tokenizer."
-encoding = tokenizer.encode(sentence)
-start, end = encoding.offsets[4]
-sentence[start:end]
-```
-
-```python out
-' test'
-```
-
-Finally, we add a byte-level decoder:
-
-```python
-tokenizer.decoder = decoders.ByteLevel()
-```
-
-and we can double-check it works properly:
-
-```python
-tokenizer.decode(encoding.ids)
-```
-
-```python out
-"Let's test this tokenizer."
-```
-
-Great! Now that we're done, we can save the tokenizer like before, and wrap it in a `PreTrainedTokenizerFast` or `GPT2TokenizerFast` if we want to use it in 🤗 Transformers:
-
-```python
-from transformers import PreTrainedTokenizerFast
-
-wrapped_tokenizer = PreTrainedTokenizerFast(
- tokenizer_object=tokenizer,
- bos_token="<|endoftext|>",
- eos_token="<|endoftext|>",
-)
-```
-
-or:
-
-```python
-from transformers import GPT2TokenizerFast
-
-wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)
-```
-
-As the last example, we'll show you how to build a Unigram tokenizer from scratch.
-
-## Building a Unigram tokenizer from scratch[[building-a-unigram-tokenizer-from-scratch]]
-
-Let's now build an XLNet tokenizer. Like for the previous tokenizers, we start by initializing a `Tokenizer` with a Unigram model:
-
-```python
-tokenizer = Tokenizer(models.Unigram())
-```
-
-Again, we could initialize this model with a vocabulary if we had one.
-
-For the normalization, XLNet uses a few replacements (which come from SentencePiece):
-
-```python
-from tokenizers import Regex
-
-tokenizer.normalizer = normalizers.Sequence(
- [
- normalizers.Replace("``", '"'),
- normalizers.Replace("''", '"'),
- normalizers.NFKD(),
- normalizers.StripAccents(),
- normalizers.Replace(Regex(" {2,}"), " "),
- ]
-)
-```
-
-This replaces `` and '' with " and any sequence of two or more spaces with a single space, as well as removing the accents in the texts to tokenize.
-
-The pre-tokenizer to use for any SentencePiece tokenizer is `Metaspace`:
-
-```python
-tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()
-```
-
-We can have a look at the pre-tokenization of an example text like before:
-
-```python
-tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!")
-```
-
-```python out
-[("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))]
-```
-
-Next is the model, which needs training. XLNet has quite a few special tokens:
-
-```python
-special_tokens = ["", "", "", "", "", "", ""]
-trainer = trainers.UnigramTrainer(
- vocab_size=25000, special_tokens=special_tokens, unk_token=""
-)
-tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
-```
-
-A very important argument not to forget for the `UnigramTrainer` is the `unk_token`. We can also pass along other arguments specific to the Unigram algorithm, such as the `shrinking_factor` for each step where we remove tokens (defaults to 0.75) or the `max_piece_length` to specify the maximum length of a given token (defaults to 16).
-
-This tokenizer can also be trained on text files:
-
-```python
-tokenizer.model = models.Unigram()
-tokenizer.train(["wikitext-2.txt"], trainer=trainer)
-```
-
-Let's have a look at the tokenization of a sample text:
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer.")
-print(encoding.tokens)
-```
-
-```python out
-['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']
-```
-
-A peculiarity of XLNet is that it puts the `` token at the end of the sentence, with a type ID of 2 (to distinguish it from the other tokens). It's padding on the left, as a result. We can deal with all the special tokens and token type IDs with a template, like for BERT, but first we have to get the IDs of the `` and `` tokens:
-
-```python
-cls_token_id = tokenizer.token_to_id("")
-sep_token_id = tokenizer.token_to_id("")
-print(cls_token_id, sep_token_id)
-```
-
-```python out
-0 1
-```
-
-The template looks like this:
-
-```python
-tokenizer.post_processor = processors.TemplateProcessing(
- single="$A:0 :0 :2",
- pair="$A:0 :0 $B:1 :1 :2",
- special_tokens=[("", sep_token_id), ("", cls_token_id)],
-)
-```
-
-And we can test it works by encoding a pair of sentences:
-
-```python
-encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!")
-print(encoding.tokens)
-print(encoding.type_ids)
-```
-
-```python out
-['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '', '▁', 'on', '▁', 'a', '▁pair',
- '▁of', '▁sentence', 's', '!', '', '']
-[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]
-```
-
-Finally, we add a `Metaspace` decoder:
-
-```python
-tokenizer.decoder = decoders.Metaspace()
-```
-
-and we're done with this tokenizer! We can save the tokenizer like before, and wrap it in a `PreTrainedTokenizerFast` or `XLNetTokenizerFast` if we want to use it in 🤗 Transformers. One thing to note when using `PreTrainedTokenizerFast` is that on top of the special tokens, we need to tell the 🤗 Transformers library to pad on the left:
-
-```python
-from transformers import PreTrainedTokenizerFast
-
-wrapped_tokenizer = PreTrainedTokenizerFast(
- tokenizer_object=tokenizer,
- bos_token="",
- eos_token="",
- unk_token="",
- pad_token="",
- cls_token="",
- sep_token="",
- mask_token="",
- padding_side="left",
-)
-```
-
-Or alternatively:
-
-```python
-from transformers import XLNetTokenizerFast
-
-wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)
-```
-
-Now that you have seen how the various building blocks are used to build existing tokenizers, you should be able to write any tokenizer you want with the 🤗 Tokenizers library and be able to use it in 🤗 Transformers.
+# Building a tokenizer, block by block[[building-a-tokenizer-block-by-block]]
+
+
+
+As we've seen in the previous sections, tokenization comprises several steps:
+
+- Normalization (any cleanup of the text that is deemed necessary, such as removing spaces or accents, Unicode normalization, etc.)
+- Pre-tokenization (splitting the input into words)
+- Running the input through the model (using the pre-tokenized words to produce a sequence of tokens)
+- Post-processing (adding the special tokens of the tokenizer, generating the attention mask and token type IDs)
+
+As a reminder, here's another look at the overall process:
+
+
+

+

+
+
+The 🤗 Tokenizers library has been built to provide several options for each of those steps, which you can mix and match together. In this section we'll see how we can build a tokenizer from scratch, as opposed to training a new tokenizer from an old one as we did in [section 2](/course/chapter6/2). You'll then be able to build any kind of tokenizer you can think of!
+
+
+
+More precisely, the library is built around a central `Tokenizer` class with the building blocks regrouped in submodules:
+
+- `normalizers` contains all the possible types of `Normalizer` you can use (complete list [here](https://huggingface.co/docs/tokenizers/api/normalizers)).
+- `pre_tokenizers` contains all the possible types of `PreTokenizer` you can use (complete list [here](https://huggingface.co/docs/tokenizers/api/pre-tokenizers)).
+- `models` contains the various types of `Model` you can use, like `BPE`, `WordPiece`, and `Unigram` (complete list [here](https://huggingface.co/docs/tokenizers/api/models)).
+- `trainers` contains all the different types of `Trainer` you can use to train your model on a corpus (one per type of model; complete list [here](https://huggingface.co/docs/tokenizers/api/trainers)).
+- `post_processors` contains the various types of `PostProcessor` you can use (complete list [here](https://huggingface.co/docs/tokenizers/api/post-processors)).
+- `decoders` contains the various types of `Decoder` you can use to decode the outputs of tokenization (complete list [here](https://huggingface.co/docs/tokenizers/components#decoders)).
+
+You can find the whole list of building blocks [here](https://huggingface.co/docs/tokenizers/components).
+
+## Acquiring a corpus[[acquiring-a-corpus]]
+
+To train our new tokenizer, we will use a small corpus of text (so the examples run fast). The steps for acquiring the corpus are similar to the ones we took at the [beginning of this chapter](/course/chapter6/2), but this time we'll use the [WikiText-2](https://huggingface.co/datasets/wikitext) dataset:
+
+```python
+from datasets import load_dataset
+
+dataset = load_dataset("wikitext", name="wikitext-2-raw-v1", split="train")
+
+
+def get_training_corpus():
+ for i in range(0, len(dataset), 1000):
+ yield dataset[i : i + 1000]["text"]
+```
+
+The function `get_training_corpus()` is a generator that will yield batches of 1,000 texts, which we will use to train the tokenizer.
+
+🤗 Tokenizers can also be trained on text files directly. Here's how we can generate a text file containing all the texts/inputs from WikiText-2 that we can use locally:
+
+```python
+with open("wikitext-2.txt", "w", encoding="utf-8") as f:
+ for i in range(len(dataset)):
+ f.write(dataset[i]["text"] + "\n")
+```
+
+Next we'll show you how to build your own BERT, GPT-2, and XLNet tokenizers, block by block. That will give us an example of each of the three main tokenization algorithms: WordPiece, BPE, and Unigram. Let's start with BERT!
+
+## Building a WordPiece tokenizer from scratch[[building-a-wordpiece-tokenizer-from-scratch]]
+
+To build a tokenizer with the 🤗 Tokenizers library, we start by instantiating a `Tokenizer` object with a `model`, then set its `normalizer`, `pre_tokenizer`, `post_processor`, and `decoder` attributes to the values we want.
+
+For this example, we'll create a `Tokenizer` with a WordPiece model:
+
+```python
+from tokenizers import (
+ decoders,
+ models,
+ normalizers,
+ pre_tokenizers,
+ processors,
+ trainers,
+ Tokenizer,
+)
+
+tokenizer = Tokenizer(models.WordPiece(unk_token="[UNK]"))
+```
+
+We have to specify the `unk_token` so the model knows what to return when it encounters characters it hasn't seen before. Other arguments we can set here include the `vocab` of our model (we're going to train the model, so we don't need to set this) and `max_input_chars_per_word`, which specifies a maximum length for each word (words longer than the value passed will be split).
+
+The first step of tokenization is normalization, so let's begin with that. Since BERT is widely used, there is a `BertNormalizer` with the classic options we can set for BERT: `lowercase` and `strip_accents`, which are self-explanatory; `clean_text` to remove all control characters and replace repeating spaces with a single one; and `handle_chinese_chars`, which places spaces around Chinese characters. To replicate the `bert-base-uncased` tokenizer, we can just set this normalizer:
+
+```python
+tokenizer.normalizer = normalizers.BertNormalizer(lowercase=True)
+```
+
+Generally speaking, however, when building a new tokenizer you won't have access to such a handy normalizer already implemented in the 🤗 Tokenizers library -- so let's see how to create the BERT normalizer by hand. The library provides a `Lowercase` normalizer and a `StripAccents` normalizer, and you can compose several normalizers using a `Sequence`:
+
+```python
+tokenizer.normalizer = normalizers.Sequence(
+ [normalizers.NFD(), normalizers.Lowercase(), normalizers.StripAccents()]
+)
+```
+
+We're also using an `NFD` Unicode normalizer, as otherwise the `StripAccents` normalizer won't properly recognize the accented characters and thus won't strip them out.
+
+As we've seen before, we can use the `normalize_str()` method of the `normalizer` to check out the effects it has on a given text:
+
+```python
+print(tokenizer.normalizer.normalize_str("Héllò hôw are ü?"))
+```
+
+```python out
+hello how are u?
+```
+
+> [!TIP]
+> **To go further** If you test the two versions of the previous normalizers on a string containing the unicode character `u"\u0085"` you will surely notice that these two normalizers are not exactly equivalent.
+> To not over-complicate the version with `normalizers.Sequence` too much , we haven't included the Regex replacements that the `BertNormalizer` requires when the `clean_text` argument is set to `True` - which is the default behavior. But don't worry: it is possible to get exactly the same normalization without using the handy `BertNormalizer` by adding two `normalizers.Replace`'s to the normalizers sequence.
+
+Next is the pre-tokenization step. Again, there is a prebuilt `BertPreTokenizer` that we can use:
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.BertPreTokenizer()
+```
+
+Or we can build it from scratch:
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.Whitespace()
+```
+
+Note that the `Whitespace` pre-tokenizer splits on whitespace and all characters that are not letters, digits, or the underscore character, so it technically splits on whitespace and punctuation:
+
+```python
+tokenizer.pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
+```
+
+```python out
+[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
+ ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
+```
+
+If you only want to split on whitespace, you should use the `WhitespaceSplit` pre-tokenizer instead:
+
+```python
+pre_tokenizer = pre_tokenizers.WhitespaceSplit()
+pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
+```
+
+```python out
+[("Let's", (0, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre-tokenizer.', (14, 28))]
+```
+
+Like with normalizers, you can use a `Sequence` to compose several pre-tokenizers:
+
+```python
+pre_tokenizer = pre_tokenizers.Sequence(
+ [pre_tokenizers.WhitespaceSplit(), pre_tokenizers.Punctuation()]
+)
+pre_tokenizer.pre_tokenize_str("Let's test my pre-tokenizer.")
+```
+
+```python out
+[('Let', (0, 3)), ("'", (3, 4)), ('s', (4, 5)), ('test', (6, 10)), ('my', (11, 13)), ('pre', (14, 17)),
+ ('-', (17, 18)), ('tokenizer', (18, 27)), ('.', (27, 28))]
+```
+
+The next step in the tokenization pipeline is running the inputs through the model. We already specified our model in the initialization, but we still need to train it, which will require a `WordPieceTrainer`. The main thing to remember when instantiating a trainer in 🤗 Tokenizers is that you need to pass it all the special tokens you intend to use -- otherwise it won't add them to the vocabulary, since they are not in the training corpus:
+
+```python
+special_tokens = ["[UNK]", "[PAD]", "[CLS]", "[SEP]", "[MASK]"]
+trainer = trainers.WordPieceTrainer(vocab_size=25000, special_tokens=special_tokens)
+```
+
+As well as specifying the `vocab_size` and `special_tokens`, we can set the `min_frequency` (the number of times a token must appear to be included in the vocabulary) or change the `continuing_subword_prefix` (if we want to use something different from `##`).
+
+To train our model using the iterator we defined earlier, we just have to execute this command:
+
+```python
+tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
+```
+
+We can also use text files to train our tokenizer, which would look like this (we reinitialize the model with an empty `WordPiece` beforehand):
+
+```python
+tokenizer.model = models.WordPiece(unk_token="[UNK]")
+tokenizer.train(["wikitext-2.txt"], trainer=trainer)
+```
+
+In both cases, we can then test the tokenizer on a text by calling the `encode()` method:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.']
+```
+
+The `encoding` obtained is an `Encoding`, which contains all the necessary outputs of the tokenizer in its various attributes: `ids`, `type_ids`, `tokens`, `offsets`, `attention_mask`, `special_tokens_mask`, and `overflowing`.
+
+The last step in the tokenization pipeline is post-processing. We need to add the `[CLS]` token at the beginning and the `[SEP]` token at the end (or after each sentence, if we have a pair of sentences). We will use a `TemplateProcessor` for this, but first we need to know the IDs of the `[CLS]` and `[SEP]` tokens in the vocabulary:
+
+```python
+cls_token_id = tokenizer.token_to_id("[CLS]")
+sep_token_id = tokenizer.token_to_id("[SEP]")
+print(cls_token_id, sep_token_id)
+```
+
+```python out
+(2, 3)
+```
+
+To write the template for the `TemplateProcessor`, we have to specify how to treat a single sentence and a pair of sentences. For both, we write the special tokens we want to use; the first (or single) sentence is represented by `$A`, while the second sentence (if encoding a pair) is represented by `$B`. For each of these (special tokens and sentences), we also specify the corresponding token type ID after a colon.
+
+The classic BERT template is thus defined as follows:
+
+```python
+tokenizer.post_processor = processors.TemplateProcessing(
+ single=f"[CLS]:0 $A:0 [SEP]:0",
+ pair=f"[CLS]:0 $A:0 [SEP]:0 $B:1 [SEP]:1",
+ special_tokens=[("[CLS]", cls_token_id), ("[SEP]", sep_token_id)],
+)
+```
+
+Note that we need to pass along the IDs of the special tokens, so the tokenizer can properly convert them to their IDs.
+
+Once this is added, going back to our previous example will give:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '.', '[SEP]']
+```
+
+And on a pair of sentences, we get the proper result:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences.")
+print(encoding.tokens)
+print(encoding.type_ids)
+```
+
+```python out
+['[CLS]', 'let', "'", 's', 'test', 'this', 'tok', '##eni', '##zer', '...', '[SEP]', 'on', 'a', 'pair', 'of', 'sentences', '.', '[SEP]']
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1]
+```
+
+We've almost finished building this tokenizer from scratch -- the last step is to include a decoder:
+
+```python
+tokenizer.decoder = decoders.WordPiece(prefix="##")
+```
+
+Let's test it on our previous `encoding`:
+
+```python
+tokenizer.decode(encoding.ids)
+```
+
+```python out
+"let's test this tokenizer... on a pair of sentences."
+```
+
+Great! We can save our tokenizer in a single JSON file like this:
+
+```python
+tokenizer.save("tokenizer.json")
+```
+
+We can then reload that file in a `Tokenizer` object with the `from_file()` method:
+
+```python
+new_tokenizer = Tokenizer.from_file("tokenizer.json")
+```
+
+To use this tokenizer in 🤗 Transformers, we have to wrap it in a `PreTrainedTokenizerFast`. We can either use the generic class or, if our tokenizer corresponds to an existing model, use that class (here, `BertTokenizerFast`). If you apply this lesson to build a brand new tokenizer, you will have to use the first option.
+
+To wrap the tokenizer in a `PreTrainedTokenizerFast`, we can either pass the tokenizer we built as a `tokenizer_object` or pass the tokenizer file we saved as `tokenizer_file`. The key thing to remember is that we have to manually set all the special tokens, since that class can't infer from the `tokenizer` object which token is the mask token, the `[CLS]` token, etc.:
+
+```python
+from transformers import PreTrainedTokenizerFast
+
+wrapped_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ # tokenizer_file="tokenizer.json", # You can load from the tokenizer file, alternatively
+ unk_token="[UNK]",
+ pad_token="[PAD]",
+ cls_token="[CLS]",
+ sep_token="[SEP]",
+ mask_token="[MASK]",
+)
+```
+
+If you are using a specific tokenizer class (like `BertTokenizerFast`), you will only need to specify the special tokens that are different from the default ones (here, none):
+
+```python
+from transformers import BertTokenizerFast
+
+wrapped_tokenizer = BertTokenizerFast(tokenizer_object=tokenizer)
+```
+
+You can then use this tokenizer like any other 🤗 Transformers tokenizer. You can save it with the `save_pretrained()` method, or upload it to the Hub with the `push_to_hub()` method.
+
+Now that we've seen how to build a WordPiece tokenizer, let's do the same for a BPE tokenizer. We'll go a bit faster since you know all the steps, and only highlight the differences.
+
+## Building a BPE tokenizer from scratch[[building-a-bpe-tokenizer-from-scratch]]
+
+Let's now build a GPT-2 tokenizer. Like for the BERT tokenizer, we start by initializing a `Tokenizer` with a BPE model:
+
+```python
+tokenizer = Tokenizer(models.BPE())
+```
+
+Also like for BERT, we could initialize this model with a vocabulary if we had one (we would need to pass the `vocab` and `merges` in this case), but since we will train from scratch, we don't need to do that. We also don't need to specify an `unk_token` because GPT-2 uses byte-level BPE, which doesn't require it.
+
+GPT-2 does not use a normalizer, so we skip that step and go directly to the pre-tokenization:
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.ByteLevel(add_prefix_space=False)
+```
+
+The option we added to `ByteLevel` here is to not add a space at the beginning of a sentence (which is the default otherwise). We can have a look at the pre-tokenization of an example text like before:
+
+```python
+tokenizer.pre_tokenizer.pre_tokenize_str("Let's test pre-tokenization!")
+```
+
+```python out
+[('Let', (0, 3)), ("'s", (3, 5)), ('Ġtest', (5, 10)), ('Ġpre', (10, 14)), ('-', (14, 15)),
+ ('tokenization', (15, 27)), ('!', (27, 28))]
+```
+
+Next is the model, which needs training. For GPT-2, the only special token is the end-of-text token:
+
+```python
+trainer = trainers.BpeTrainer(vocab_size=25000, special_tokens=["<|endoftext|>"])
+tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
+```
+
+Like with the `WordPieceTrainer`, as well as the `vocab_size` and `special_tokens`, we can specify the `min_frequency` if we want to, or if we have an end-of-word suffix (like ``), we can set it with `end_of_word_suffix`.
+
+This tokenizer can also be trained on text files:
+
+```python
+tokenizer.model = models.BPE()
+tokenizer.train(["wikitext-2.txt"], trainer=trainer)
+```
+
+Let's have a look at the tokenization of a sample text:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['L', 'et', "'", 's', 'Ġtest', 'Ġthis', 'Ġto', 'ken', 'izer', '.']
+```
+
+We apply the byte-level post-processing for the GPT-2 tokenizer as follows:
+
+```python
+tokenizer.post_processor = processors.ByteLevel(trim_offsets=False)
+```
+
+The `trim_offsets = False` option indicates to the post-processor that we should leave the offsets of tokens that begin with 'Ġ' as they are: this way the start of the offsets will point to the space before the word, not the first character of the word (since the space is technically part of the token). Let's have a look at the result with the text we just encoded, where `'Ġtest'` is the token at index 4:
+
+```python
+sentence = "Let's test this tokenizer."
+encoding = tokenizer.encode(sentence)
+start, end = encoding.offsets[4]
+sentence[start:end]
+```
+
+```python out
+' test'
+```
+
+Finally, we add a byte-level decoder:
+
+```python
+tokenizer.decoder = decoders.ByteLevel()
+```
+
+and we can double-check it works properly:
+
+```python
+tokenizer.decode(encoding.ids)
+```
+
+```python out
+"Let's test this tokenizer."
+```
+
+Great! Now that we're done, we can save the tokenizer like before, and wrap it in a `PreTrainedTokenizerFast` or `GPT2TokenizerFast` if we want to use it in 🤗 Transformers:
+
+```python
+from transformers import PreTrainedTokenizerFast
+
+wrapped_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ bos_token="<|endoftext|>",
+ eos_token="<|endoftext|>",
+)
+```
+
+or:
+
+```python
+from transformers import GPT2TokenizerFast
+
+wrapped_tokenizer = GPT2TokenizerFast(tokenizer_object=tokenizer)
+```
+
+As the last example, we'll show you how to build a Unigram tokenizer from scratch.
+
+## Building a Unigram tokenizer from scratch[[building-a-unigram-tokenizer-from-scratch]]
+
+Let's now build an XLNet tokenizer. Like for the previous tokenizers, we start by initializing a `Tokenizer` with a Unigram model:
+
+```python
+tokenizer = Tokenizer(models.Unigram())
+```
+
+Again, we could initialize this model with a vocabulary if we had one.
+
+For the normalization, XLNet uses a few replacements (which come from SentencePiece):
+
+```python
+from tokenizers import Regex
+
+tokenizer.normalizer = normalizers.Sequence(
+ [
+ normalizers.Replace("``", '"'),
+ normalizers.Replace("''", '"'),
+ normalizers.NFKD(),
+ normalizers.StripAccents(),
+ normalizers.Replace(Regex(" {2,}"), " "),
+ ]
+)
+```
+
+This replaces `` and '' with " and any sequence of two or more spaces with a single space, as well as removing the accents in the texts to tokenize.
+
+The pre-tokenizer to use for any SentencePiece tokenizer is `Metaspace`:
+
+```python
+tokenizer.pre_tokenizer = pre_tokenizers.Metaspace()
+```
+
+We can have a look at the pre-tokenization of an example text like before:
+
+```python
+tokenizer.pre_tokenizer.pre_tokenize_str("Let's test the pre-tokenizer!")
+```
+
+```python out
+[("▁Let's", (0, 5)), ('▁test', (5, 10)), ('▁the', (10, 14)), ('▁pre-tokenizer!', (14, 29))]
+```
+
+Next is the model, which needs training. XLNet has quite a few special tokens:
+
+```python
+special_tokens = ["", "", "", "", "", "", ""]
+trainer = trainers.UnigramTrainer(
+ vocab_size=25000, special_tokens=special_tokens, unk_token=""
+)
+tokenizer.train_from_iterator(get_training_corpus(), trainer=trainer)
+```
+
+A very important argument not to forget for the `UnigramTrainer` is the `unk_token`. We can also pass along other arguments specific to the Unigram algorithm, such as the `shrinking_factor` for each step where we remove tokens (defaults to 0.75) or the `max_piece_length` to specify the maximum length of a given token (defaults to 16).
+
+This tokenizer can also be trained on text files:
+
+```python
+tokenizer.model = models.Unigram()
+tokenizer.train(["wikitext-2.txt"], trainer=trainer)
+```
+
+Let's have a look at the tokenization of a sample text:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer.")
+print(encoding.tokens)
+```
+
+```python out
+['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.']
+```
+
+A peculiarity of XLNet is that it puts the `` token at the end of the sentence, with a type ID of 2 (to distinguish it from the other tokens). It's padding on the left, as a result. We can deal with all the special tokens and token type IDs with a template, like for BERT, but first we have to get the IDs of the `` and `` tokens:
+
+```python
+cls_token_id = tokenizer.token_to_id("")
+sep_token_id = tokenizer.token_to_id("")
+print(cls_token_id, sep_token_id)
+```
+
+```python out
+0 1
+```
+
+The template looks like this:
+
+```python
+tokenizer.post_processor = processors.TemplateProcessing(
+ single="$A:0 :0 :2",
+ pair="$A:0 :0 $B:1 :1 :2",
+ special_tokens=[("", sep_token_id), ("", cls_token_id)],
+)
+```
+
+And we can test it works by encoding a pair of sentences:
+
+```python
+encoding = tokenizer.encode("Let's test this tokenizer...", "on a pair of sentences!")
+print(encoding.tokens)
+print(encoding.type_ids)
+```
+
+```python out
+['▁Let', "'", 's', '▁test', '▁this', '▁to', 'ken', 'izer', '.', '.', '.', '', '▁', 'on', '▁', 'a', '▁pair',
+ '▁of', '▁sentence', 's', '!', '', '']
+[0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 2]
+```
+
+Finally, we add a `Metaspace` decoder:
+
+```python
+tokenizer.decoder = decoders.Metaspace()
+```
+
+and we're done with this tokenizer! We can save the tokenizer like before, and wrap it in a `PreTrainedTokenizerFast` or `XLNetTokenizerFast` if we want to use it in 🤗 Transformers. One thing to note when using `PreTrainedTokenizerFast` is that on top of the special tokens, we need to tell the 🤗 Transformers library to pad on the left:
+
+```python
+from transformers import PreTrainedTokenizerFast
+
+wrapped_tokenizer = PreTrainedTokenizerFast(
+ tokenizer_object=tokenizer,
+ bos_token="",
+ eos_token="",
+ unk_token="",
+ pad_token="",
+ cls_token="",
+ sep_token="",
+ mask_token="",
+ padding_side="left",
+)
+```
+
+Or alternatively:
+
+```python
+from transformers import XLNetTokenizerFast
+
+wrapped_tokenizer = XLNetTokenizerFast(tokenizer_object=tokenizer)
+```
+
+Now that you have seen how the various building blocks are used to build existing tokenizers, you should be able to write any tokenizer you want with the 🤗 Tokenizers library and be able to use it in 🤗 Transformers.
diff --git a/chapters/en/chapter6/9.mdx b/chapters/en/chapter6/9.mdx
index 288c4864b..e85a4dbc2 100644
--- a/chapters/en/chapter6/9.mdx
+++ b/chapters/en/chapter6/9.mdx
@@ -1,16 +1,16 @@
-# Tokenizers, check![[tokenizers-check]]
-
-
-
-Great job finishing this chapter!
-
-After this deep dive into tokenizers, you should:
-
-- Be able to train a new tokenizer using an old one as a template
-- Understand how to use offsets to map tokens' positions to their original span of text
-- Know the differences between BPE, WordPiece, and Unigram
-- Be able to mix and match the blocks provided by the 🤗 Tokenizers library to build your own tokenizer
-- Be able to use that tokenizer inside the 🤗 Transformers library
+# Tokenizers, check![[tokenizers-check]]
+
+
+
+Great job finishing this chapter!
+
+After this deep dive into tokenizers, you should:
+
+- Be able to train a new tokenizer using an old one as a template
+- Understand how to use offsets to map tokens' positions to their original span of text
+- Know the differences between BPE, WordPiece, and Unigram
+- Be able to mix and match the blocks provided by the 🤗 Tokenizers library to build your own tokenizer
+- Be able to use that tokenizer inside the 🤗 Transformers library
diff --git a/chapters/en/chapter7/1.mdx b/chapters/en/chapter7/1.mdx
index 068cc90ae..f3940b0bb 100644
--- a/chapters/en/chapter7/1.mdx
+++ b/chapters/en/chapter7/1.mdx
@@ -1,37 +1,37 @@
-
-
-# Introduction[[introduction]]
-
-
-
-In [Chapter 3](/course/chapter3), you saw how to fine-tune a model for text classification. In this chapter, we will tackle the following common language tasks that are essential for working with both traditional NLP models and modern LLMs:
-
-- Token classification
-- Masked language modeling (like BERT)
-- Summarization
-- Translation
-- Causal language modeling pretraining (like GPT-2)
-- Question answering
-
-These fundamental tasks form the foundation of how Large Language Models (LLMs) work and understanding them is crucial for effectively working with today's most advanced language models.
-
-{#if fw === 'pt'}
-
-To do this, you'll need to leverage everything you learned about the `Trainer` API and the 🤗 Accelerate library in [Chapter 3](/course/chapter3), the 🤗 Datasets library in [Chapter 5](/course/chapter5), and the 🤗 Tokenizers library in [Chapter 6](/course/chapter6). We'll also upload our results to the Model Hub, like we did in [Chapter 4](/course/chapter4), so this is really the chapter where everything comes together!
-
-Each section can be read independently and will show you how to train a model with the `Trainer` API or with your own training loop, using 🤗 Accelerate. Feel free to skip either part and focus on the one that interests you the most: the `Trainer` API is great for fine-tuning or training your model without worrying about what's going on behind the scenes, while the training loop with `Accelerate` will let you customize any part you want more easily.
-
-{:else}
-
-To do this, you'll need to leverage everything you learned about training models with the Keras API in [Chapter 3](/course/chapter3), the 🤗 Datasets library in [Chapter 5](/course/chapter5), and the 🤗 Tokenizers library in [Chapter 6](/course/chapter6). We'll also upload our results to the Model Hub, like we did in [Chapter 4](/course/chapter4), so this is really the chapter where everything comes together!
-
-Each section can be read independently.
-
-{/if}
-
-
-> [!TIP]
-> If you read the sections in sequence, you will notice that they have quite a bit of code and prose in common. The repetition is intentional, to allow you to dip in (or come back later) to any task that interests you and find a complete working example.
+
+
+# Introduction[[introduction]]
+
+
+
+In [Chapter 3](/course/chapter3), you saw how to fine-tune a model for text classification. In this chapter, we will tackle the following common language tasks that are essential for working with both traditional NLP models and modern LLMs:
+
+- Token classification
+- Masked language modeling (like BERT)
+- Summarization
+- Translation
+- Causal language modeling pretraining (like GPT-2)
+- Question answering
+
+These fundamental tasks form the foundation of how Large Language Models (LLMs) work and understanding them is crucial for effectively working with today's most advanced language models.
+
+{#if fw === 'pt'}
+
+To do this, you'll need to leverage everything you learned about the `Trainer` API and the 🤗 Accelerate library in [Chapter 3](/course/chapter3), the 🤗 Datasets library in [Chapter 5](/course/chapter5), and the 🤗 Tokenizers library in [Chapter 6](/course/chapter6). We'll also upload our results to the Model Hub, like we did in [Chapter 4](/course/chapter4), so this is really the chapter where everything comes together!
+
+Each section can be read independently and will show you how to train a model with the `Trainer` API or with your own training loop, using 🤗 Accelerate. Feel free to skip either part and focus on the one that interests you the most: the `Trainer` API is great for fine-tuning or training your model without worrying about what's going on behind the scenes, while the training loop with `Accelerate` will let you customize any part you want more easily.
+
+{:else}
+
+To do this, you'll need to leverage everything you learned about training models with the Keras API in [Chapter 3](/course/chapter3), the 🤗 Datasets library in [Chapter 5](/course/chapter5), and the 🤗 Tokenizers library in [Chapter 6](/course/chapter6). We'll also upload our results to the Model Hub, like we did in [Chapter 4](/course/chapter4), so this is really the chapter where everything comes together!
+
+Each section can be read independently.
+
+{/if}
+
+
+> [!TIP]
+> If you read the sections in sequence, you will notice that they have quite a bit of code and prose in common. The repetition is intentional, to allow you to dip in (or come back later) to any task that interests you and find a complete working example.
diff --git a/chapters/en/chapter7/2.mdx b/chapters/en/chapter7/2.mdx
index 65845cbb0..684e805f6 100644
--- a/chapters/en/chapter7/2.mdx
+++ b/chapters/en/chapter7/2.mdx
@@ -1,957 +1,957 @@
-
-
-# Token classification[[token-classification]]
-
-{#if fw === 'pt'}
-
-
-
-{:else}
-
-
-
-{/if}
-
-The first application we'll explore is token classification. This generic task encompasses any problem that can be formulated as "attributing a label to each token in a sentence," such as:
-
-- **Named entity recognition (NER)**: Find the entities (such as persons, locations, or organizations) in a sentence. This can be formulated as attributing a label to each token by having one class per entity and one class for "no entity."
-- **Part-of-speech tagging (POS)**: Mark each word in a sentence as corresponding to a particular part of speech (such as noun, verb, adjective, etc.).
-- **Chunking**: Find the tokens that belong to the same entity. This task (which can be combined with POS or NER) can be formulated as attributing one label (usually `B-`) to any tokens that are at the beginning of a chunk, another label (usually `I-`) to tokens that are inside a chunk, and a third label (usually `O`) to tokens that don't belong to any chunk.
-
-
-
-Of course, there are many other types of token classification problem; those are just a few representative examples. In this section, we will fine-tune a model (BERT) on a NER task, which will then be able to compute predictions like this one:
-
-
-
-
-