Compare commits
27 Commits
Author | SHA1 | Date |
---|---|---|
|
3edee269d4 | |
|
e42ebff63d | |
|
450277f3a2 | |
|
28e11145ae | |
|
7f205d2e8c | |
|
e5cfa315fe | |
|
b3cacaebec | |
|
e51c411719 | |
|
2d9071c02b | |
|
0e3803ddf0 | |
|
62c272344e | |
|
b87af57674 | |
|
8770d10e11 | |
|
7d60eaeccc | |
|
35b0b2de8c | |
|
098eb16190 | |
|
fee3287214 | |
|
b177a44507 | |
|
94dc7d092b | |
|
dae3fdac13 | |
|
36405486a1 | |
|
20ef4d238d | |
|
87bd3f61e9 | |
|
a1e2a3643d | |
|
c0cbd84d20 | |
|
c68a39f180 | |
|
24158fb704 |
|
@ -1,57 +0,0 @@
|
||||||
name: "Setup Env Vars"
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
- name: Get Commit SHA (For Push Event)
|
|
||||||
if: ${{ github.event_name == 'push' }}
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
SHA: ${{ github.sha }}
|
|
||||||
git_repo_full: ${{ github.repository }}
|
|
||||||
pr_number: ""
|
|
||||||
run: |
|
|
||||||
short_sha=$(git rev-parse --short "$SHA")
|
|
||||||
echo "SHORT_SHA=$short_sha" >> $GITHUB_ENV
|
|
||||||
echo "PR_NUMBER=$pr_number" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
target_branch=${GITHUB_REF##*/}
|
|
||||||
echo "TARGET_BRANCH=$target_branch" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
repo_name=${git_repo_full##*/}
|
|
||||||
echo "REPO_NAME=$repo_name" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
IFS='-' read -ra name_parts <<< "$repo_name"
|
|
||||||
echo "LANG=${name_parts[1]}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
job_name=${repo_name}/${target_branch}
|
|
||||||
echo "JOB_NAME=$job_name" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
echo "CACHE_DIR=ci_cache_push" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
|
|
||||||
- name: Get Commit SHA (For Pull Request)
|
|
||||||
if: ${{ github.event_name == 'pull_request_target' }}
|
|
||||||
shell: bash
|
|
||||||
env:
|
|
||||||
SHA: ${{ github.event.pull_request.head.sha }}
|
|
||||||
target_branch: ${{ github.event.pull_request.base.ref }}
|
|
||||||
git_repo_full: ${{ github.event.pull_request.base.repo.full_name }}
|
|
||||||
pr_number: PR-${{ github.event.number }}
|
|
||||||
run: |
|
|
||||||
short_sha=$(git rev-parse --short "$SHA")
|
|
||||||
echo "SHORT_SHA=$short_sha" >> $GITHUB_ENV
|
|
||||||
echo "PR_NUMBER=$pr_number" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
echo "TARGET_BRANCH=$target_branch" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
repo_name=${git_repo_full##*/}
|
|
||||||
echo "REPO_NAME=$repo_name" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
IFS='-' read -ra name_parts <<< "$repo_name"
|
|
||||||
echo "LANG=${name_parts[1]}" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
job_name=${repo_name}/${pr_number}/${short_sha}
|
|
||||||
echo "JOB_NAME=$job_name" >> $GITHUB_ENV
|
|
||||||
|
|
||||||
echo "CACHE_DIR=ci_cache_pr" >> $GITHUB_ENV
|
|
|
@ -1,91 +0,0 @@
|
||||||
name: "Submit Job to AWS Batch"
|
|
||||||
inputs:
|
|
||||||
job-type:
|
|
||||||
required: true
|
|
||||||
job-name:
|
|
||||||
required: true
|
|
||||||
work-dir:
|
|
||||||
required: false
|
|
||||||
default: .
|
|
||||||
command:
|
|
||||||
required: true
|
|
||||||
|
|
||||||
runs:
|
|
||||||
using: "composite"
|
|
||||||
steps:
|
|
||||||
- name: Configure AWS Credentials
|
|
||||||
uses: aws-actions/configure-aws-credentials@v2
|
|
||||||
with:
|
|
||||||
role-to-assume: arn:aws:iam::650140442593:role/D2L_CI_Batch
|
|
||||||
role-duration-seconds: 14400 # this requires changing max session duration to 4hrs in AWS Console for D2L_CI_Batch
|
|
||||||
aws-region: us-west-2
|
|
||||||
|
|
||||||
- name: Install dependencies
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
pip install boto3
|
|
||||||
|
|
||||||
- name: Check for Actor Permissions
|
|
||||||
id: check
|
|
||||||
continue-on-error: true
|
|
||||||
uses: prince-chrismc/check-actor-permissions-action@88995341900ae4e7aa248df51d32aeb981cc62f5 # v2
|
|
||||||
with:
|
|
||||||
github_token: ${{ github.token }}
|
|
||||||
permission: write
|
|
||||||
|
|
||||||
- name: Submit Job (For Push on development branches)
|
|
||||||
if: ${{ github.event_name == 'push' && github.ref != 'refs/heads/release' && github.ref != 'refs/heads/classic' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "Start submitting job for a Push Event on a Development Branch"
|
|
||||||
# Add "-push" for all these jobs to use elevated push level job-def permissions
|
|
||||||
python ./ci/submit-job.py --job-type ${{ inputs.job-type }}-push \
|
|
||||||
--name ${{ inputs.job-name }}-'${{ github.ref }}' \
|
|
||||||
--source-ref '${{ github.ref }}' \
|
|
||||||
--work-dir ${{ inputs.work-dir }} \
|
|
||||||
--remote https://github.com/'${{ github.repository }}' \
|
|
||||||
--command "${{ inputs.command }}" \
|
|
||||||
--safe-to-use-script \
|
|
||||||
--wait
|
|
||||||
|
|
||||||
- name: Submit Job (For Push on Release/Classic)
|
|
||||||
if: ${{ github.event_name == 'push' && (github.ref == 'refs/heads/release' || github.ref == 'refs/heads/classic') }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "Start submitting job for a Push Event on Release/Classic Branch"
|
|
||||||
# Add "-release" for all these jobs to use elevated release level job-def permissions
|
|
||||||
python ./ci/submit-job.py --job-type ${{ inputs.job-type }}-release \
|
|
||||||
--name ${{ inputs.job-name }}-'${{ github.ref }}' \
|
|
||||||
--source-ref '${{ github.ref }}' \
|
|
||||||
--work-dir ${{ inputs.work-dir }} \
|
|
||||||
--remote https://github.com/'${{ github.repository }}' \
|
|
||||||
--command "${{ inputs.command }}" \
|
|
||||||
--safe-to-use-script \
|
|
||||||
--wait
|
|
||||||
|
|
||||||
- name: Submit Job (For Pull Request Safe Scripts)
|
|
||||||
if: ${{ github.event_name == 'pull_request_target' && steps.check.outputs.permitted == 'true' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "Start submitting job for PR Safe Scripts"
|
|
||||||
python ./ci/submit-job.py --job-type ${{ inputs.job-type }} \
|
|
||||||
--name ${{ inputs.job-name }}-PR#'${{ github.event.number }}' \
|
|
||||||
--source-ref '${{ github.event.pull_request.head.sha }}' \
|
|
||||||
--work-dir ${{ inputs.work-dir }} \
|
|
||||||
--remote https://github.com/'${{ github.event.pull_request.head.repo.full_name }}' \
|
|
||||||
--command "${{ inputs.command }}" \
|
|
||||||
--safe-to-use-script \
|
|
||||||
--wait
|
|
||||||
|
|
||||||
- name: Submit Job (For Pull Request Not Safe Scripts)
|
|
||||||
if: ${{ github.event_name == 'pull_request_target' && steps.check.outputs.permitted != 'true' }}
|
|
||||||
shell: bash
|
|
||||||
run: |
|
|
||||||
echo "Start submitting job for PR Unsafe Scripts"
|
|
||||||
python ./ci/submit-job.py --job-type ${{ inputs.job-type }} \
|
|
||||||
--name ${{ inputs.job-name }}-PR#'${{ github.event.number }}' \
|
|
||||||
--source-ref '${{ github.event.pull_request.head.sha }}' \
|
|
||||||
--work-dir ${{ inputs.work-dir }} \
|
|
||||||
--remote https://github.com/'${{ github.event.pull_request.head.repo.full_name }}' \
|
|
||||||
--command "${{ inputs.command }}" \
|
|
||||||
--wait
|
|
|
@ -1,45 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Move all scripts related to html here!
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
REPO_NAME="$1" # Eg. 'd2l-zh'
|
|
||||||
TARGET_BRANCH="$2" # Eg. 'master' ; if PR raised to master
|
|
||||||
JOB_NAME="$3" # Eg. 'd2l-zh/master' or 'd2l-zh/PR-2453/21be1a4'
|
|
||||||
LANG="$4" # Eg. 'en','zh' etc.
|
|
||||||
CACHE_DIR="$5" # Eg. 'ci_cache_pr' or 'ci_cache_push'
|
|
||||||
|
|
||||||
pip3 install d2l==0.17.6
|
|
||||||
mkdir _build
|
|
||||||
|
|
||||||
source $(dirname "$0")/utils.sh
|
|
||||||
|
|
||||||
# Move aws copy commands for cache restore outside
|
|
||||||
measure_command_time "aws s3 sync s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build _build --delete --quiet --exclude 'eval*/data/*'"
|
|
||||||
|
|
||||||
# Build D2L Website
|
|
||||||
./.github/workflow_scripts/build_html.sh
|
|
||||||
|
|
||||||
# Build PDFs
|
|
||||||
d2lbook build pdf
|
|
||||||
d2lbook build pdf --tab pytorch
|
|
||||||
|
|
||||||
|
|
||||||
# Check if the JOB_NAME is "$REPO_NAME/release"
|
|
||||||
if [[ "$JOB_NAME" == "$REPO_NAME/release" ]]; then
|
|
||||||
|
|
||||||
# Setup D2L Bot
|
|
||||||
source $(dirname "$0")/setup_git.sh
|
|
||||||
setup_git
|
|
||||||
|
|
||||||
d2lbook build pkg
|
|
||||||
d2lbook deploy html pdf pkg colab sagemaker slides --s3 "s3://${LANG}-v2.d2l.ai/"
|
|
||||||
|
|
||||||
else
|
|
||||||
# Run d2lbook preview deployment
|
|
||||||
d2lbook deploy html pdf --s3 "s3://preview.d2l.ai/${JOB_NAME}/"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Move aws copy commands for cache store outside
|
|
||||||
measure_command_time "aws s3 sync _build s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build --acl public-read --quiet --exclude 'eval*/data/*'"
|
|
|
@ -1,44 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
# Used to capture status exit of build eval command
|
|
||||||
ss=0
|
|
||||||
|
|
||||||
REPO_NAME="$1" # Eg. 'd2l-zh'
|
|
||||||
TARGET_BRANCH="$2" # Eg. 'master' ; if PR raised to master
|
|
||||||
CACHE_DIR="$3" # Eg. 'ci_cache_pr' or 'ci_cache_push'
|
|
||||||
|
|
||||||
pip3 install d2l==0.17.6
|
|
||||||
mkdir _build
|
|
||||||
|
|
||||||
source $(dirname "$0")/utils.sh
|
|
||||||
|
|
||||||
# Move sanity check outside
|
|
||||||
d2lbook build outputcheck tabcheck
|
|
||||||
|
|
||||||
# Move aws copy commands for cache restore outside
|
|
||||||
if [ "$DISABLE_CACHE" = "false" ]; then
|
|
||||||
echo "Retrieving mxnet build cache from "$CACHE_DIR""
|
|
||||||
measure_command_time "aws s3 sync s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build/eval/ _build/eval/ --delete --quiet --exclude 'data/*'"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# MXNet training for the following notebooks is slow in the container;
|
|
||||||
# Setting NTHREADS=4 below seems to fix the issue:
|
|
||||||
# 1. chapter_multilayer-perceptrons/dropout.md
|
|
||||||
# 2. chapter_multilayer-perceptrons/mlp-implementation.md
|
|
||||||
# 3. chapter_linear-classification/softmax-regression-concise.md
|
|
||||||
# 4. chapter_linear-classification/softmax-regression-scratch.md
|
|
||||||
export MXNET_CPU_WORKER_NTHREADS=4
|
|
||||||
# Continue the script even if some notebooks in build fail to
|
|
||||||
# make sure that cache is copied to s3 for the successful notebooks
|
|
||||||
d2lbook build eval || ((ss=1))
|
|
||||||
|
|
||||||
# Move aws copy commands for cache store outside
|
|
||||||
echo "Upload mxnet build cache to s3"
|
|
||||||
measure_command_time "aws s3 sync _build s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build --acl public-read --quiet --exclude 'eval*/data/*'"
|
|
||||||
|
|
||||||
# Exit with a non-zero status if evaluation failed
|
|
||||||
if [ "$ss" -ne 0 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
|
@ -1,37 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
# Used to capture status exit of build eval command
|
|
||||||
ss=0
|
|
||||||
|
|
||||||
REPO_NAME="$1" # Eg. 'd2l-zh'
|
|
||||||
TARGET_BRANCH="$2" # Eg. 'master' ; if PR raised to master
|
|
||||||
CACHE_DIR="$3" # Eg. 'ci_cache_pr' or 'ci_cache_push'
|
|
||||||
|
|
||||||
pip3 install d2l==0.17.6
|
|
||||||
mkdir _build
|
|
||||||
|
|
||||||
source $(dirname "$0")/utils.sh
|
|
||||||
|
|
||||||
# Move sanity check outside
|
|
||||||
d2lbook build outputcheck tabcheck
|
|
||||||
|
|
||||||
# Move aws copy commands for cache restore outside
|
|
||||||
if [ "$DISABLE_CACHE" = "false" ]; then
|
|
||||||
echo "Retrieving paddle build cache from "$CACHE_DIR""
|
|
||||||
measure_command_time "aws s3 sync s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build/eval_paddle/ _build/eval_paddle/ --delete --quiet --exclude 'data/*'"
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Continue the script even if some notebooks in build fail to
|
|
||||||
# make sure that cache is copied to s3 for the successful notebooks
|
|
||||||
d2lbook build eval --tab paddle || ((ss=1))
|
|
||||||
|
|
||||||
# Move aws copy commands for cache store outside
|
|
||||||
echo "Upload paddle build cache to s3"
|
|
||||||
measure_command_time "aws s3 sync _build s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build --acl public-read --quiet --exclude 'eval*/data/*'"
|
|
||||||
|
|
||||||
# Exit with a non-zero status if evaluation failed
|
|
||||||
if [ "$ss" -ne 0 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
|
@ -1,41 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
# Used to capture status exit of build eval command
|
|
||||||
ss=0
|
|
||||||
|
|
||||||
REPO_NAME="$1" # Eg. 'd2l-zh'
|
|
||||||
TARGET_BRANCH="$2" # Eg. 'master' ; if PR raised to master
|
|
||||||
CACHE_DIR="$3" # Eg. 'ci_cache_pr' or 'ci_cache_push'
|
|
||||||
|
|
||||||
pip3 install d2l==0.17.6
|
|
||||||
pip3 install numpy==1.24.4 # numpy version installed by d2l==0.17.6 is too old
|
|
||||||
mkdir _build
|
|
||||||
|
|
||||||
source $(dirname "$0")/utils.sh
|
|
||||||
|
|
||||||
# Move sanity check outside
|
|
||||||
d2lbook build outputcheck tabcheck
|
|
||||||
|
|
||||||
# Move aws copy commands for cache restore outside
|
|
||||||
if [ "$DISABLE_CACHE" = "false" ]; then
|
|
||||||
echo "Retrieving pytorch build cache from "$CACHE_DIR""
|
|
||||||
measure_command_time "aws s3 sync s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build/eval_pytorch/ _build/eval_pytorch/ --delete --quiet --exclude 'data/*'"
|
|
||||||
echo "Retrieving pytorch slides cache from "$CACHE_DIR""
|
|
||||||
aws s3 sync s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build/slides _build/slides --delete --quiet --exclude 'data/*'
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Continue the script even if some notebooks in build fail to
|
|
||||||
# make sure that cache is copied to s3 for the successful notebooks
|
|
||||||
d2lbook build eval --tab pytorch || ((ss=1))
|
|
||||||
d2lbook build slides --tab pytorch
|
|
||||||
|
|
||||||
# Move aws copy commands for cache store outside
|
|
||||||
echo "Upload pytorch build cache to s3"
|
|
||||||
measure_command_time "aws s3 sync _build s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build --acl public-read --quiet --exclude 'eval*/data/*'"
|
|
||||||
|
|
||||||
# Exit with a non-zero status if evaluation failed
|
|
||||||
if [ "$ss" -ne 0 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
|
@ -1,39 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
set -ex
|
|
||||||
|
|
||||||
# Used to capture status exit of build eval command
|
|
||||||
ss=0
|
|
||||||
|
|
||||||
REPO_NAME="$1" # Eg. 'd2l-zh'
|
|
||||||
TARGET_BRANCH="$2" # Eg. 'master' ; if PR raised to master
|
|
||||||
CACHE_DIR="$3" # Eg. 'ci_cache_pr' or 'ci_cache_push'
|
|
||||||
|
|
||||||
pip3 install d2l==0.17.6
|
|
||||||
mkdir _build
|
|
||||||
|
|
||||||
source $(dirname "$0")/utils.sh
|
|
||||||
|
|
||||||
# Move sanity check outside
|
|
||||||
d2lbook build outputcheck tabcheck
|
|
||||||
|
|
||||||
# Move aws copy commands for cache restore outside
|
|
||||||
if [ "$DISABLE_CACHE" = "false" ]; then
|
|
||||||
echo "Retrieving tensorflow build cache from "$CACHE_DIR""
|
|
||||||
measure_command_time "aws s3 sync s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build/eval_tensorflow/ _build/eval_tensorflow/ --delete --quiet --exclude 'data/*'"
|
|
||||||
fi
|
|
||||||
|
|
||||||
export TF_CPP_MIN_LOG_LEVEL=3
|
|
||||||
export TF_FORCE_GPU_ALLOW_GROWTH=true
|
|
||||||
# Continue the script even if some notebooks in build fail to
|
|
||||||
# make sure that cache is copied to s3 for the successful notebooks
|
|
||||||
d2lbook build eval --tab tensorflow || ((ss=1))
|
|
||||||
|
|
||||||
# Move aws copy commands for cache store outside
|
|
||||||
echo "Upload tensorflow build cache to s3"
|
|
||||||
measure_command_time "aws s3 sync _build s3://preview.d2l.ai/"$CACHE_DIR"/"$REPO_NAME"-"$TARGET_BRANCH"/_build --acl public-read --quiet --exclude 'eval*/data/*'"
|
|
||||||
|
|
||||||
# Exit with a non-zero status if evaluation failed
|
|
||||||
if [ "$ss" -ne 0 ]; then
|
|
||||||
exit 1
|
|
||||||
fi
|
|
|
@ -1,18 +0,0 @@
|
||||||
function setup_git {
|
|
||||||
# Turn off logging
|
|
||||||
set +x
|
|
||||||
mkdir -p $HOME/.ssh
|
|
||||||
echo "yes" | ssh-keyscan -t rsa github.com >> ~/.ssh/known_hosts
|
|
||||||
|
|
||||||
# Retrieve the SSH key securely from AWS Secrets Manager
|
|
||||||
GIT_SSH_KEY=$(aws secretsmanager get-secret-value --secret-id d2l_bot_github --query SecretString --output text --region us-west-2)
|
|
||||||
|
|
||||||
# Write the SSH key to a file
|
|
||||||
echo "$GIT_SSH_KEY" > $HOME/.ssh/id_rsa
|
|
||||||
chmod 600 $HOME/.ssh/id_rsa
|
|
||||||
|
|
||||||
git config --global user.name "d2l-bot"
|
|
||||||
git config --global user.email "100248899+d2l-bot@users.noreply.github.com"
|
|
||||||
|
|
||||||
echo "Successfully Configured Bot"
|
|
||||||
}
|
|
|
@ -1,26 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# By default, all builds are cached
|
|
||||||
DISABLE_CACHE=false # Eg. 'true' or 'false'
|
|
||||||
|
|
||||||
|
|
||||||
# Function to measure command execution time
|
|
||||||
measure_command_time() {
|
|
||||||
local command="$1"
|
|
||||||
|
|
||||||
# Start timing
|
|
||||||
local start_time=$(date +%s)
|
|
||||||
|
|
||||||
# Run the command
|
|
||||||
eval "$command"
|
|
||||||
|
|
||||||
# Calculate the time taken
|
|
||||||
local end_time=$(date +%s)
|
|
||||||
local elapsed_time=$((end_time - start_time))
|
|
||||||
|
|
||||||
# Format the elapsed time for display
|
|
||||||
local formatted_time=$(printf "%02dhr %02dmin %02dsec" $((elapsed_time / 3600)) $(((elapsed_time % 3600) / 60)) $((elapsed_time % 60)))
|
|
||||||
|
|
||||||
# Print the elapsed time
|
|
||||||
echo "Time taken for $command: $formatted_time"
|
|
||||||
}
|
|
|
@ -1,88 +0,0 @@
|
||||||
name: Build Docker Image
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
image_torch:
|
|
||||||
type: boolean
|
|
||||||
description: Build PyTorch Image
|
|
||||||
image_tf:
|
|
||||||
type: boolean
|
|
||||||
description: Build TensorFlow Image
|
|
||||||
image_mxnet:
|
|
||||||
type: boolean
|
|
||||||
description: Build MXNet Image
|
|
||||||
image_paddle:
|
|
||||||
type: boolean
|
|
||||||
description: Build Paddle Image
|
|
||||||
image_builder:
|
|
||||||
type: boolean
|
|
||||||
description: Build D2L Builder Image
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_docker_image:
|
|
||||||
name: Build D2L Docker Images
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
working-directory: ./ci/docker
|
|
||||||
steps:
|
|
||||||
- uses: actions/checkout@v3
|
|
||||||
- name: Configure AWS Credentials
|
|
||||||
uses: aws-actions/configure-aws-credentials@v2
|
|
||||||
with:
|
|
||||||
role-to-assume: arn:aws:iam::650140442593:role/D2L_CI_Docker
|
|
||||||
role-duration-seconds: 3600
|
|
||||||
aws-region: us-west-2
|
|
||||||
|
|
||||||
- name: Build D2L PyTorch Image
|
|
||||||
if: github.event.inputs.image_torch == 'true'
|
|
||||||
run: |
|
|
||||||
chmod +x ./login_ecr.sh; ./login_ecr.sh
|
|
||||||
docker build -f Dockerfile.d2l-zh-torch -t d2l-containers:d2l-zh-torch-latest .
|
|
||||||
docker tag d2l-containers:d2l-zh-torch-latest 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-zh-torch-latest
|
|
||||||
docker push 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-zh-torch-latest
|
|
||||||
# Clean up to reclaim space
|
|
||||||
echo "y" | docker system prune -a
|
|
||||||
|
|
||||||
- name: Build D2L TensorFlow Image
|
|
||||||
if: github.event.inputs.image_tf == 'true'
|
|
||||||
run: |
|
|
||||||
chmod +x ./login_ecr.sh; ./login_ecr.sh
|
|
||||||
docker build -f Dockerfile.d2l-zh-tf -t d2l-containers:d2l-zh-tensorflow-latest .
|
|
||||||
docker tag d2l-containers:d2l-zh-tensorflow-latest 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-zh-tensorflow-latest
|
|
||||||
docker push 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-zh-tensorflow-latest
|
|
||||||
# Clean up to reclaim space
|
|
||||||
echo "y" | docker system prune -a
|
|
||||||
|
|
||||||
- name: Build D2L MXNet Image
|
|
||||||
if: github.event.inputs.image_mxnet == 'true'
|
|
||||||
run: |
|
|
||||||
chmod +x ./login_ecr.sh; ./login_ecr.sh
|
|
||||||
docker build -f Dockerfile.d2l-zh-mxnet -t d2l-containers:d2l-zh-mxnet-latest .
|
|
||||||
docker tag d2l-containers:d2l-zh-mxnet-latest 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-zh-mxnet-latest
|
|
||||||
docker push 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-zh-mxnet-latest
|
|
||||||
# Clean up to reclaim space
|
|
||||||
echo "y" | docker system prune -a
|
|
||||||
|
|
||||||
- name: Build D2L Paddle Image
|
|
||||||
if: github.event.inputs.image_paddle == 'true'
|
|
||||||
run: |
|
|
||||||
chmod +x ./login_ecr.sh; ./login_ecr.sh
|
|
||||||
docker build -f Dockerfile.d2l-zh-paddle -t d2l-containers:d2l-zh-paddle-latest .
|
|
||||||
docker tag d2l-containers:d2l-zh-paddle-latest 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-zh-paddle-latest
|
|
||||||
docker push 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-zh-paddle-latest
|
|
||||||
# Clean up to reclaim space
|
|
||||||
echo "y" | docker system prune -a
|
|
||||||
|
|
||||||
- name: Build D2L CPU Builder Image
|
|
||||||
if: github.event.inputs.image_builder == 'true'
|
|
||||||
run: |
|
|
||||||
chmod +x ./login_ecr.sh; ./login_ecr.sh
|
|
||||||
docker build -f Dockerfile.d2l-builder -t d2l-containers:d2l-builder-latest .
|
|
||||||
docker tag d2l-containers:d2l-builder-latest 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-builder-latest
|
|
||||||
docker push 650140442593.dkr.ecr.us-west-2.amazonaws.com/d2l-containers:d2l-builder-latest
|
|
|
@ -1,142 +0,0 @@
|
||||||
name: Continuous Integration
|
|
||||||
|
|
||||||
on:
|
|
||||||
# Triggers the workflow on push or pull request events only for the specified branches
|
|
||||||
push:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- release
|
|
||||||
pull_request_target:
|
|
||||||
branches:
|
|
||||||
- master
|
|
||||||
- release
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
pull-requests: write
|
|
||||||
|
|
||||||
defaults:
|
|
||||||
run:
|
|
||||||
shell: bash
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
build_torch:
|
|
||||||
name: Build PyTorch
|
|
||||||
if: "github.repository == 'd2l-ai/d2l-zh' && !contains(github.event.head_commit.message, '[skip torch]') && !contains(github.event.head_commit.message, '[skip frameworks]')"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Env Vars
|
|
||||||
uses: ./.github/actions/setup_env_vars
|
|
||||||
- name: Evaluate PyTorch on AWS Batch
|
|
||||||
uses: ./.github/actions/submit-job
|
|
||||||
with:
|
|
||||||
job-type: ci-gpu-torch
|
|
||||||
job-name: D2L-Build-PyTorch
|
|
||||||
command: chmod +x ./.github/workflow_scripts/build_pytorch.sh && ./.github/workflow_scripts/build_pytorch.sh "${{ env.REPO_NAME }}" "${{ env.TARGET_BRANCH }}" "${{ env.CACHE_DIR }}"
|
|
||||||
- name: Terminate Batch Job on Cancellation
|
|
||||||
if: ${{ cancelled() && env.Batch_JobID }}
|
|
||||||
run: |
|
|
||||||
echo "Terminating Submitted AWS Batch Job: "${{ env.Batch_JobID }}""
|
|
||||||
aws batch terminate-job --job-id "${{ env.Batch_JobID }}" --reason "Job terminated by cancelled workflow"
|
|
||||||
|
|
||||||
build_tf:
|
|
||||||
name: Build Tensorflow
|
|
||||||
if: "github.repository == 'd2l-ai/d2l-zh' && !contains(github.event.head_commit.message, '[skip tf]') && !contains(github.event.head_commit.message, '[skip frameworks]')"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Env Vars
|
|
||||||
uses: ./.github/actions/setup_env_vars
|
|
||||||
- name: Evaluate Tensorflow on AWS Batch
|
|
||||||
uses: ./.github/actions/submit-job
|
|
||||||
with:
|
|
||||||
job-type: ci-gpu-tf
|
|
||||||
job-name: D2L-Build-Tensorflow
|
|
||||||
command: chmod +x ./.github/workflow_scripts/build_tf.sh && ./.github/workflow_scripts/build_tf.sh "${{ env.REPO_NAME }}" "${{ env.TARGET_BRANCH }}" "${{ env.CACHE_DIR }}"
|
|
||||||
- name: Terminate Batch Job on Cancellation
|
|
||||||
if: ${{ cancelled() && env.Batch_JobID }}
|
|
||||||
run: |
|
|
||||||
echo "Terminating Submitted AWS Batch Job: "${{ env.Batch_JobID }}""
|
|
||||||
aws batch terminate-job --job-id "${{ env.Batch_JobID }}" --reason "Job terminated by cancelled workflow"
|
|
||||||
|
|
||||||
build_mxnet:
|
|
||||||
name: Build MXNet
|
|
||||||
if: "github.repository == 'd2l-ai/d2l-zh' && !contains(github.event.head_commit.message, '[skip mxnet]') && !contains(github.event.head_commit.message, '[skip frameworks]')"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Env Vars
|
|
||||||
uses: ./.github/actions/setup_env_vars
|
|
||||||
- name: Evaluate MXNet on AWS Batch
|
|
||||||
uses: ./.github/actions/submit-job
|
|
||||||
with:
|
|
||||||
job-type: ci-gpu-mxnet
|
|
||||||
job-name: D2L-Build-MXNet
|
|
||||||
command: chmod +x ./.github/workflow_scripts/build_mxnet.sh && ./.github/workflow_scripts/build_mxnet.sh "${{ env.REPO_NAME }}" "${{ env.TARGET_BRANCH }}" "${{ env.CACHE_DIR }}"
|
|
||||||
- name: Terminate Batch Job on Cancellation
|
|
||||||
if: ${{ cancelled() && env.Batch_JobID }}
|
|
||||||
run: |
|
|
||||||
echo "Terminating Submitted AWS Batch Job: "${{ env.Batch_JobID }}""
|
|
||||||
aws batch terminate-job --job-id "${{ env.Batch_JobID }}" --reason "Job terminated by cancelled workflow"
|
|
||||||
|
|
||||||
build_paddle:
|
|
||||||
name: Build Paddle
|
|
||||||
if: "github.repository == 'd2l-ai/d2l-zh' && !contains(github.event.head_commit.message, '[skip paddle]') && !contains(github.event.head_commit.message, '[skip frameworks]')"
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Env Vars
|
|
||||||
uses: ./.github/actions/setup_env_vars
|
|
||||||
- name: Evaluate Paddle on AWS Batch
|
|
||||||
uses: ./.github/actions/submit-job
|
|
||||||
with:
|
|
||||||
job-type: ci-gpu-paddle
|
|
||||||
job-name: D2L-Build-Paddle
|
|
||||||
command: chmod +x ./.github/workflow_scripts/build_paddle.sh && ./.github/workflow_scripts/build_paddle.sh "${{ env.REPO_NAME }}" "${{ env.TARGET_BRANCH }}" "${{ env.CACHE_DIR }}"
|
|
||||||
- name: Terminate Batch Job on Cancellation
|
|
||||||
if: ${{ cancelled() && env.Batch_JobID }}
|
|
||||||
run: |
|
|
||||||
echo "Terminating Submitted AWS Batch Job: "${{ env.Batch_JobID }}""
|
|
||||||
aws batch terminate-job --job-id "${{ env.Batch_JobID }}" --reason "Job terminated by cancelled workflow"
|
|
||||||
|
|
||||||
build_and_deploy:
|
|
||||||
name: Build Website/PDF & Publish
|
|
||||||
needs: [build_torch, build_tf, build_mxnet, build_paddle]
|
|
||||||
if: |
|
|
||||||
always() &&
|
|
||||||
github.repository == 'd2l-ai/d2l-zh' &&
|
|
||||||
!contains(github.event.head_commit.message, '[skip builder]') &&
|
|
||||||
(needs.build_torch.result == 'success' || needs.build_torch.result == 'skipped') &&
|
|
||||||
(needs.build_tf.result == 'success' || needs.build_tf.result == 'skipped') &&
|
|
||||||
(needs.build_mxnet.result == 'success' || needs.build_mxnet.result == 'skipped') &&
|
|
||||||
(needs.build_paddle.result == 'success' || needs.build_paddle.result == 'skipped')
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Checkout repository
|
|
||||||
uses: actions/checkout@v3
|
|
||||||
- name: Setup Env Vars
|
|
||||||
uses: ./.github/actions/setup_env_vars
|
|
||||||
- name: Build Website & PDFs on AWS Batch
|
|
||||||
uses: ./.github/actions/submit-job
|
|
||||||
with:
|
|
||||||
job-type: ci-cpu
|
|
||||||
job-name: D2L-Builder
|
|
||||||
command: chmod +x ./.github/workflow_scripts/build_and_deploy.sh ./.github/workflow_scripts/build_html.sh && ./.github/workflow_scripts/build_and_deploy.sh "${{ env.REPO_NAME }}" "${{ env.TARGET_BRANCH }}" "${{ env.JOB_NAME }}" "${{ env.LANG }}" "${{ env.CACHE_DIR }}"
|
|
||||||
- name: Terminate Batch Job on Cancellation
|
|
||||||
if: ${{ cancelled() && env.Batch_JobID }}
|
|
||||||
run: |
|
|
||||||
echo "Terminating Submitted AWS Batch Job: "${{ env.Batch_JobID }}""
|
|
||||||
aws batch terminate-job --job-id "${{ env.Batch_JobID }}" --reason "Job terminated by cancelled workflow"
|
|
||||||
- name: Comment on PR
|
|
||||||
if: ${{ github.event_name == 'pull_request_target' }}
|
|
||||||
uses: peter-evans/create-or-update-comment@38e799a33166c9a254f2e3660d4d49ecd67eb80c # v3
|
|
||||||
with:
|
|
||||||
issue-number: ${{ github.event.number }}
|
|
||||||
body: |
|
|
||||||
Job PR-${{ github.event.number }}-${{ env.SHORT_SHA }} is done.
|
|
||||||
Check the results at http://preview.d2l.ai/${{ env.JOB_NAME }}
|
|
|
@ -1,39 +0,0 @@
|
||||||
name: Clear CI Cache
|
|
||||||
|
|
||||||
on:
|
|
||||||
workflow_dispatch:
|
|
||||||
inputs:
|
|
||||||
target_branch:
|
|
||||||
type: choice
|
|
||||||
description: Choose the cache associated branch
|
|
||||||
options:
|
|
||||||
- master
|
|
||||||
- release
|
|
||||||
|
|
||||||
cache_dir:
|
|
||||||
type: choice
|
|
||||||
description: Choose cache type, pull requests or push events
|
|
||||||
options:
|
|
||||||
- ci_cache_pr
|
|
||||||
- ci_cache_push
|
|
||||||
|
|
||||||
permissions:
|
|
||||||
id-token: write
|
|
||||||
contents: read
|
|
||||||
|
|
||||||
jobs:
|
|
||||||
clear_cache:
|
|
||||||
name: Clear CI Cache
|
|
||||||
runs-on: ubuntu-latest
|
|
||||||
steps:
|
|
||||||
- name: Configure AWS Credentials
|
|
||||||
uses: aws-actions/configure-aws-credentials@v2
|
|
||||||
with:
|
|
||||||
role-to-assume: arn:aws:iam::650140442593:role/D2L_CI_Clear_Cache
|
|
||||||
role-duration-seconds: 3600
|
|
||||||
aws-region: us-west-2
|
|
||||||
- name: Delete Cache
|
|
||||||
run: |
|
|
||||||
CACHE_S3_URL="s3://preview.d2l.ai/${{ github.event.inputs.cache_dir }}/d2l-zh-${{ github.event.inputs.target_branch }}/"
|
|
||||||
echo "Removing cache in $CACHE_S3_URL"
|
|
||||||
aws s3 rm --recursive $CACHE_S3_URL
|
|
|
@ -0,0 +1,94 @@
|
||||||
|
stage("Build and Publish") {
|
||||||
|
// such as d2l-en and d2l-zh
|
||||||
|
def REPO_NAME = env.JOB_NAME.split('/')[0]
|
||||||
|
// such as en and zh
|
||||||
|
def LANG = REPO_NAME.split('-')[1]
|
||||||
|
// The current branch or the branch this PR will merge into
|
||||||
|
def TARGET_BRANCH = env.CHANGE_TARGET ? env.CHANGE_TARGET : env.BRANCH_NAME
|
||||||
|
// such as d2l-en-master
|
||||||
|
def TASK = REPO_NAME + '-' + TARGET_BRANCH
|
||||||
|
node('d2l-worker') {
|
||||||
|
ws("workspace/${TASK}") {
|
||||||
|
checkout scm
|
||||||
|
// conda environment
|
||||||
|
def ENV_NAME = "${TASK}-${EXECUTOR_NUMBER}";
|
||||||
|
|
||||||
|
sh label: "Build Environment", script: """set -ex
|
||||||
|
conda env update -n ${ENV_NAME} -f static/build.yml
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
pip uninstall -y d2lbook
|
||||||
|
pip install git+https://github.com/d2l-ai/d2l-book
|
||||||
|
pip list
|
||||||
|
nvidia-smi
|
||||||
|
"""
|
||||||
|
|
||||||
|
sh label: "Sanity Check", script: """set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
d2lbook build outputcheck tabcheck
|
||||||
|
"""
|
||||||
|
|
||||||
|
sh label: "Execute Notebooks", script: """set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
./static/cache.sh restore _build/eval/data
|
||||||
|
d2lbook build eval
|
||||||
|
./static/cache.sh store _build/eval/data
|
||||||
|
"""
|
||||||
|
|
||||||
|
sh label: "Execute Notebooks [Pytorch]", script: """set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
./static/cache.sh restore _build/eval_pytorch/data
|
||||||
|
d2lbook build eval --tab pytorch
|
||||||
|
d2lbook build slides --tab pytorch
|
||||||
|
./static/cache.sh store _build/eval_pytorch/data
|
||||||
|
"""
|
||||||
|
|
||||||
|
sh label: "Execute Notebooks [Tensorflow]", script: """set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
./static/cache.sh restore _build/eval_tensorflow/data
|
||||||
|
export TF_CPP_MIN_LOG_LEVEL=3
|
||||||
|
export TF_FORCE_GPU_ALLOW_GROWTH=true
|
||||||
|
d2lbook build eval --tab tensorflow
|
||||||
|
./static/cache.sh store _build/eval_tensorflow/data
|
||||||
|
"""
|
||||||
|
|
||||||
|
sh label: "Execute Notebooks [Paddlepaddle]", script: """set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
./static/cache.sh restore _build/eval_paddle/data
|
||||||
|
d2lbook build eval --tab paddle
|
||||||
|
./static/cache.sh store _build/eval_paddle/data
|
||||||
|
"""
|
||||||
|
|
||||||
|
sh label:"Build HTML", script:"""set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
./static/build_html.sh
|
||||||
|
"""
|
||||||
|
|
||||||
|
sh label:"Build PDF", script:"""set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
d2lbook build pdf
|
||||||
|
"""
|
||||||
|
|
||||||
|
sh label:"Build Pytorch PDF", script:"""set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
d2lbook build pdf --tab pytorch
|
||||||
|
"""
|
||||||
|
|
||||||
|
if (env.BRANCH_NAME == 'release') {
|
||||||
|
sh label:"Release", script:"""set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
d2lbook build pkg
|
||||||
|
d2lbook deploy html pdf slides pkg colab sagemaker --s3 s3://${LANG}-v2.d2l.ai
|
||||||
|
"""
|
||||||
|
|
||||||
|
} else {
|
||||||
|
sh label:"Publish", script:"""set -ex
|
||||||
|
conda activate ${ENV_NAME}
|
||||||
|
d2lbook deploy html pdf --s3 s3://preview.d2l.ai/${JOB_NAME}/
|
||||||
|
"""
|
||||||
|
if (env.BRANCH_NAME.startsWith("PR-")) {
|
||||||
|
pullRequest.comment("Job ${JOB_NAME}/${BUILD_NUMBER} is complete. \nCheck the results at http://preview.d2l.ai/${JOB_NAME}/")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,5 +1,7 @@
|
||||||
# 动手学深度学习(Dive into Deep Learning,D2L.ai)
|
# 动手学深度学习(Dive into Deep Learning,D2L.ai)
|
||||||
|
|
||||||
|
[](http://ci.d2l.ai/job/d2l-zh/job/master/)
|
||||||
|
|
||||||
[第二版:zh.D2L.ai](https://zh.d2l.ai) | [第一版:zh-v1.D2L.ai](https://zh-v1.d2l.ai/) | 安装和使用书中源代码: [第二版](https://zh.d2l.ai/chapter_installation/index.html) [第一版](https://zh-v1.d2l.ai/chapter_prerequisite/install.html)
|
[第二版:zh.D2L.ai](https://zh.d2l.ai) | [第一版:zh-v1.D2L.ai](https://zh-v1.d2l.ai/) | 安装和使用书中源代码: [第二版](https://zh.d2l.ai/chapter_installation/index.html) [第一版](https://zh-v1.d2l.ai/chapter_prerequisite/install.html)
|
||||||
|
|
||||||
<h5 align="center"><i>理解深度学习的最佳方法是学以致用。</i></h5>
|
<h5 align="center"><i>理解深度学习的最佳方法是学以致用。</i></h5>
|
||||||
|
@ -28,12 +30,11 @@
|
||||||
如果本书对你有帮助,请Star (★) 本仓库或引用本书的英文版:
|
如果本书对你有帮助,请Star (★) 本仓库或引用本书的英文版:
|
||||||
|
|
||||||
```
|
```
|
||||||
@book{zhang2023dive,
|
@article{zhang2021dive,
|
||||||
title={Dive into Deep Learning},
|
title={Dive into Deep Learning},
|
||||||
author={Zhang, Aston and Lipton, Zachary C. and Li, Mu and Smola, Alexander J.},
|
author={Zhang, Aston and Lipton, Zachary C. and Li, Mu and Smola, Alexander J.},
|
||||||
publisher={Cambridge University Press},
|
journal={arXiv preprint arXiv:2106.11342},
|
||||||
note={\url{https://D2L.ai}},
|
year={2021}
|
||||||
year={2023}
|
|
||||||
}
|
}
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,7 @@
|
||||||
|
|
||||||
## 数据并行训练
|
## 数据并行训练
|
||||||
|
|
||||||
让我们回顾一下在分布式架构中数据并行的训练方法,因为在实践中它的实现相对简单,因此本节将排除其他内容只对其进行介绍。由于当今的GPU拥有大量的显存,因此在实际场景中(不包括图深度学习)只有数据并行这种并行训练策略值得推荐。图 :numref:`fig_parameterserver`描述了在 :numref:`sec_multi_gpu`中实现的数据并行的变体。其中的关键是梯度的聚合需要在单个GPU(GPU 0)上完成,然后再将更新后的参数广播给所有GPU。
|
让我们回顾一下在分布式架构中数据并行的训练方法,因为在实践中它的实现相对简单,因此本节将排除其他内容只对其进行介绍。由于当今的GPU拥有大量的显存,因此在实际场景中(不包括图深度学习)只有数据并行这种并行训练策略值得推荐。图 :numref:`fig_parameterserver`描述了在 :numref:`sec_multi_gpu`中实现的数据并行的变体。其中的关键是梯度的聚合需要在GPU 0上完成,然后再将更新后的参数广播给所有GPU。
|
||||||
|
|
||||||

|

|
||||||
:label:`fig_parameterserver`
|
:label:`fig_parameterserver`
|
||||||
|
@ -64,7 +64,7 @@
|
||||||

|

|
||||||
:label:`fig_ps_multimachine`
|
:label:`fig_ps_multimachine`
|
||||||
|
|
||||||
以上这些操作似乎都相当简单,而且事实上它们可以在一台机器内高效地执行,但是当我们考虑多台机器时,就会发现中央的参数服务器成为了瓶颈。毕竟,每个服务器的带宽是有限的,因此对$m$个工作节点来说,将所有梯度发送到服务器所需的时间是$\mathcal{O}(m)$。我们也可以通过将参数服务器数量增加到$n$来突破这一障碍。此时,每个服务器只需要存储$\mathcal{O}(1/n)$个参数,因此更新和优化的总时间变为$\mathcal{O}(m/n)$。这两个数字的匹配会产生稳定的伸缩性,而不用在乎我们需要处理多少工作节点。在实际应用中,我们使用同一台机器既作为工作节点还作为服务器。设计说明请参考 :numref:`fig_ps_multips`(技术细节请参考 :cite:`Li.Andersen.Park.ea.2014`)。特别是,确保多台机器只在没有不合理延迟的情况下工作是相当困难的。
|
以上这些操作似乎都相当简单,而且事实上它们可以在一台机器内高效地执行,但是当我们考虑多台机器时,就会发现中央的参数服务器成为了瓶颈。毕竟,每个服务器的带宽是有限的,因此对$m$个工作节点来说,将所有梯度发送到服务器所需的时间是$\mathcal{O}(m)$。我们也可以通过将参数服务器数量增加到$n$来突破这一障碍。此时,每个服务器只需要存储$\mathcal{O}(1/n)$个参数,因此更新和优化的总时间变为$\mathcal{O}(m/n)$。这两个数字的匹配会产生稳定的伸缩性,而不用在乎我们需要处理多少工作节点。在实际应用中,我们使用同一台机器既作为工作节点还作为服务器。设计说明请参考 :numref:`fig_ps_multips`(技术细节请参考 :cite:`Li.Andersen.Park.ea.2014`)。特别是,确保多台机器只在没有不合理延迟的情况下工作是相当困难的。我们在下面忽略了关于阻塞的细节,只简单介绍一下同步和异步(unsynchronized)更新。
|
||||||
|
|
||||||

|

|
||||||
:label:`fig_ps_multips`
|
:label:`fig_ps_multips`
|
||||||
|
|
|
@ -244,7 +244,7 @@ $$
|
||||||
通常,Xavier初始化从均值为零,方差
|
通常,Xavier初始化从均值为零,方差
|
||||||
$\sigma^2 = \frac{2}{n_\mathrm{in} + n_\mathrm{out}}$
|
$\sigma^2 = \frac{2}{n_\mathrm{in} + n_\mathrm{out}}$
|
||||||
的高斯分布中采样权重。
|
的高斯分布中采样权重。
|
||||||
我们也可以将其改为选择从均匀分布中抽取权重时的方差。
|
我们也可以利用Xavier的直觉来选择从均匀分布中抽取权重时的方差。
|
||||||
注意均匀分布$U(-a, a)$的方差为$\frac{a^2}{3}$。
|
注意均匀分布$U(-a, a)$的方差为$\frac{a^2}{3}$。
|
||||||
将$\frac{a^2}{3}$代入到$\sigma^2$的条件中,将得到初始化值域:
|
将$\frac{a^2}{3}$代入到$\sigma^2$的条件中,将得到初始化值域:
|
||||||
|
|
||||||
|
|
|
@ -50,7 +50,7 @@ $$\sum_{i\in\mathcal{V}} \sum_{j\in\mathcal{V}} h(x_{ij}) \left(\mathbf{u}_j^\to
|
||||||
|
|
||||||
我们也可以从另一个角度来理解GloVe模型。使用 :numref:`subsec_skipgram-global`中的相同符号,设$p_{ij} \stackrel{\mathrm{def}}{=} P(w_j \mid w_i)$为生成上下文词$w_j$的条件概率,给定$w_i$作为语料库中的中心词。 :numref:`tab_glove`根据大量语料库的统计数据,列出了给定单词“ice”和“steam”的共现概率及其比值。
|
我们也可以从另一个角度来理解GloVe模型。使用 :numref:`subsec_skipgram-global`中的相同符号,设$p_{ij} \stackrel{\mathrm{def}}{=} P(w_j \mid w_i)$为生成上下文词$w_j$的条件概率,给定$w_i$作为语料库中的中心词。 :numref:`tab_glove`根据大量语料库的统计数据,列出了给定单词“ice”和“steam”的共现概率及其比值。
|
||||||
|
|
||||||
大型语料库中的词-词共现概率及其比值(根据 :cite:`Pennington.Socher.Manning.2014`中的表1改编)
|
:大型语料库中的词-词共现概率及其比值(根据 :cite:`Pennington.Socher.Manning.2014`中的表1改编)
|
||||||
|
|
||||||
|$w_k$=|solid|gas|water|fashion|
|
|$w_k$=|solid|gas|water|fashion|
|
||||||
|:--|:-|:-|:-|:-|
|
|:--|:-|:-|:-|:-|
|
||||||
|
|
|
@ -144,7 +144,7 @@ d2l.train_ch11(adadelta, init_adadelta_states(feature_dim),
|
||||||
{'rho': 0.9}, data_iter, feature_dim);
|
{'rho': 0.9}, data_iter, feature_dim);
|
||||||
```
|
```
|
||||||
|
|
||||||
为了简洁实现,我们只需使用高级API中的Adadelta算法。
|
为了简洁实现,我们只需使用`Trainer`类中的`adadelta`算法。
|
||||||
|
|
||||||
```{.python .input}
|
```{.python .input}
|
||||||
d2l.train_concise_ch11('adadelta', {'rho': 0.9}, data_iter)
|
d2l.train_concise_ch11('adadelta', {'rho': 0.9}, data_iter)
|
||||||
|
|
|
@ -235,10 +235,7 @@ def train_2d(trainer, steps=20, f_grad=None): #@save
|
||||||
results.append((x1, x2))
|
results.append((x1, x2))
|
||||||
print(f'epoch {i + 1}, x1: {float(x1):f}, x2: {float(x2):f}')
|
print(f'epoch {i + 1}, x1: {float(x1):f}, x2: {float(x2):f}')
|
||||||
return results
|
return results
|
||||||
```
|
|
||||||
|
|
||||||
```{.python .input}
|
|
||||||
#@tab mxnet, tensorflow
|
|
||||||
def show_trace_2d(f, results): #@save
|
def show_trace_2d(f, results): #@save
|
||||||
"""显示优化过程中2D变量的轨迹"""
|
"""显示优化过程中2D变量的轨迹"""
|
||||||
d2l.set_figsize()
|
d2l.set_figsize()
|
||||||
|
@ -250,19 +247,6 @@ def show_trace_2d(f, results): #@save
|
||||||
d2l.plt.ylabel('x2')
|
d2l.plt.ylabel('x2')
|
||||||
```
|
```
|
||||||
|
|
||||||
```{.python .input}
|
|
||||||
#@tab pytorch
|
|
||||||
def show_trace_2d(f, results): #@save
|
|
||||||
"""显示优化过程中2D变量的轨迹"""
|
|
||||||
d2l.set_figsize()
|
|
||||||
d2l.plt.plot(*zip(*results), '-o', color='#ff7f0e')
|
|
||||||
x1, x2 = d2l.meshgrid(d2l.arange(-5.5, 1.0, 0.1),
|
|
||||||
d2l.arange(-3.0, 1.0, 0.1), indexing='ij')
|
|
||||||
d2l.plt.contour(x1, x2, f(x1, x2), colors='#1f77b4')
|
|
||||||
d2l.plt.xlabel('x1')
|
|
||||||
d2l.plt.ylabel('x2')
|
|
||||||
```
|
|
||||||
|
|
||||||
```{.python .input}
|
```{.python .input}
|
||||||
#@tab paddle
|
#@tab paddle
|
||||||
def train_2d(trainer, steps=20, f_grad=None): #@save
|
def train_2d(trainer, steps=20, f_grad=None): #@save
|
||||||
|
|
|
@ -75,8 +75,8 @@ def f_grad(x1, x2): # 目标函数的梯度
|
||||||
def sgd(x1, x2, s1, s2, f_grad):
|
def sgd(x1, x2, s1, s2, f_grad):
|
||||||
g1, g2 = f_grad(x1, x2)
|
g1, g2 = f_grad(x1, x2)
|
||||||
# 模拟有噪声的梯度
|
# 模拟有噪声的梯度
|
||||||
g1 += d2l.normal(0.0, 1, (1,)).item()
|
g1 += d2l.normal(0.0, 1, (1,))
|
||||||
g2 += d2l.normal(0.0, 1, (1,)).item()
|
g2 += d2l.normal(0.0, 1, (1,))
|
||||||
eta_t = eta * lr()
|
eta_t = eta * lr()
|
||||||
return (x1 - eta_t * g1, x2 - eta_t * g2, 0, 0)
|
return (x1 - eta_t * g1, x2 - eta_t * g2, 0, 0)
|
||||||
```
|
```
|
||||||
|
|
|
@ -79,7 +79,7 @@ print(inputs)
|
||||||
```{.python .input}
|
```{.python .input}
|
||||||
from mxnet import np
|
from mxnet import np
|
||||||
|
|
||||||
X, y = np.array(inputs.to_numpy(dtype=float)), np.array(outputs.to_numpy(dtype=float))
|
X, y = np.array(inputs.values), np.array(outputs.values)
|
||||||
X, y
|
X, y
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -87,8 +87,7 @@ X, y
|
||||||
#@tab pytorch
|
#@tab pytorch
|
||||||
import torch
|
import torch
|
||||||
|
|
||||||
X = torch.tensor(inputs.to_numpy(dtype=float))
|
X, y = torch.tensor(inputs.values), torch.tensor(outputs.values)
|
||||||
y = torch.tensor(outputs.to_numpy(dtype=float))
|
|
||||||
X, y
|
X, y
|
||||||
```
|
```
|
||||||
|
|
||||||
|
@ -96,8 +95,7 @@ X, y
|
||||||
#@tab tensorflow
|
#@tab tensorflow
|
||||||
import tensorflow as tf
|
import tensorflow as tf
|
||||||
|
|
||||||
X = tf.constant(inputs.to_numpy(dtype=float))
|
X, y = tf.constant(inputs.values), tf.constant(outputs.values)
|
||||||
y = tf.constant(outputs.to_numpy(dtype=float))
|
|
||||||
X, y
|
X, y
|
||||||
```
|
```
|
||||||
|
|
||||||
|
|
|
@ -1,39 +0,0 @@
|
||||||
FROM ubuntu:latest
|
|
||||||
|
|
||||||
RUN adduser --disabled-password --disabled-login ci
|
|
||||||
WORKDIR /home/ci
|
|
||||||
|
|
||||||
# Add d2l_job script
|
|
||||||
ADD d2l_job.sh .
|
|
||||||
RUN chmod +x d2l_job.sh; chown ci d2l_job.sh
|
|
||||||
|
|
||||||
# Copy git timesync for caching
|
|
||||||
ADD git-timesync /home/ci/.local/bin/
|
|
||||||
RUN chmod +x /home/ci/.local/bin/git-timesync
|
|
||||||
|
|
||||||
RUN apt-get update && apt-get -y install build-essential git wget
|
|
||||||
|
|
||||||
# Install pdf dependencies
|
|
||||||
RUN DEBIAN_FRONTEND=noninteractive apt-get install -y texlive-full
|
|
||||||
RUN apt-get install -y librsvg2-bin xindy pandoc
|
|
||||||
|
|
||||||
# Install fonts
|
|
||||||
RUN wget https://raw.githubusercontent.com/d2l-ai/utils/master/install_fonts.sh
|
|
||||||
# Remove "sudo " from the fonts script
|
|
||||||
RUN sed -i 's/sudo //g' install_fonts.sh
|
|
||||||
RUN chmod +x install_fonts.sh; ./install_fonts.sh
|
|
||||||
|
|
||||||
RUN apt-get install -y python3 python3-pip python-is-python3
|
|
||||||
|
|
||||||
# Allow permissions for pip installations and git-timesync
|
|
||||||
RUN chown -R ci:ci /home/ci/.local
|
|
||||||
|
|
||||||
USER ci
|
|
||||||
|
|
||||||
ENV PATH="/home/ci/.local/bin:$PATH"
|
|
||||||
|
|
||||||
# Install d2lbook using pip
|
|
||||||
# Install all libraries (cpu) to make sure API reference works for PDF builds
|
|
||||||
RUN pip3 install d2lbook==1.0.0 torch torchvision tensorflow mxnet
|
|
||||||
|
|
||||||
CMD ["/bin/bash"]
|
|
|
@ -1,31 +0,0 @@
|
||||||
# Use MXNet 1.9.1 (April 2023)
|
|
||||||
FROM nvcr.io/nvidia/mxnet:23.04-py3
|
|
||||||
|
|
||||||
RUN adduser --disabled-password --disabled-login ci
|
|
||||||
WORKDIR /home/ci
|
|
||||||
|
|
||||||
# Copy d2l_job script
|
|
||||||
ADD d2l_job.sh .
|
|
||||||
RUN chmod +x d2l_job.sh; chown ci d2l_job.sh
|
|
||||||
|
|
||||||
# Copy git timesync for caching
|
|
||||||
ADD git-timesync /home/ci/.local/bin/
|
|
||||||
RUN chmod +x /home/ci/.local/bin/git-timesync
|
|
||||||
|
|
||||||
# Allow permissions for pip installations and git-timesync
|
|
||||||
RUN chown -R ci:ci /home/ci/.local
|
|
||||||
|
|
||||||
# Allow write permissions for downloading data to opt/mxnet/data
|
|
||||||
RUN chown -R ci:ci /opt
|
|
||||||
|
|
||||||
USER ci
|
|
||||||
|
|
||||||
ENV PATH="/home/ci/.local/bin:$PATH"
|
|
||||||
|
|
||||||
# Install d2lbook using pip
|
|
||||||
RUN pip3 install d2lbook==1.0.0
|
|
||||||
|
|
||||||
# Python script to print framework versions
|
|
||||||
ADD print_versions.py .
|
|
||||||
|
|
||||||
CMD ["/bin/bash"]
|
|
|
@ -1,31 +0,0 @@
|
||||||
# Use Paddle 2.3.2 (Dec 2022)
|
|
||||||
FROM nvcr.io/nvidia/paddlepaddle:22.12-py3
|
|
||||||
|
|
||||||
RUN adduser --disabled-password --disabled-login ci
|
|
||||||
WORKDIR /home/ci
|
|
||||||
|
|
||||||
# Copy d2l_job script
|
|
||||||
ADD d2l_job.sh .
|
|
||||||
RUN chmod +x d2l_job.sh; chown ci d2l_job.sh
|
|
||||||
|
|
||||||
# opencv dependencies required by paddle
|
|
||||||
RUN apt-get update && apt-get install libgl1 -y
|
|
||||||
|
|
||||||
# Copy git timesync for caching
|
|
||||||
ADD git-timesync /home/ci/.local/bin/
|
|
||||||
RUN chmod +x /home/ci/.local/bin/git-timesync
|
|
||||||
|
|
||||||
# Allow permissions for pip installations and git-timesync
|
|
||||||
RUN chown -R ci:ci /home/ci/.local
|
|
||||||
|
|
||||||
USER ci
|
|
||||||
|
|
||||||
ENV PATH="/home/ci/.local/bin:$PATH"
|
|
||||||
|
|
||||||
# Install d2lbook using pip + paddlepaddle dependencies
|
|
||||||
RUN pip3 install d2lbook==1.0.0 "opencv-python==4.6.0.66"
|
|
||||||
|
|
||||||
# Python script to print framework versions
|
|
||||||
ADD print_versions.py .
|
|
||||||
|
|
||||||
CMD ["/bin/bash"]
|
|
|
@ -1,28 +0,0 @@
|
||||||
# Use Tensorflow 2.9.1 (Sept 2023)
|
|
||||||
FROM nvcr.io/nvidia/tensorflow:22.09-tf2-py3
|
|
||||||
|
|
||||||
RUN adduser --disabled-password --disabled-login ci
|
|
||||||
WORKDIR /home/ci
|
|
||||||
|
|
||||||
# Copy d2l_job script
|
|
||||||
ADD d2l_job.sh .
|
|
||||||
RUN chmod +x d2l_job.sh; chown ci d2l_job.sh
|
|
||||||
|
|
||||||
# Copy git timesync for caching
|
|
||||||
ADD git-timesync /home/ci/.local/bin/
|
|
||||||
RUN chmod +x /home/ci/.local/bin/git-timesync
|
|
||||||
|
|
||||||
# Allow permissions for pip installations and git-timesync
|
|
||||||
RUN chown -R ci:ci /home/ci/.local
|
|
||||||
|
|
||||||
USER ci
|
|
||||||
|
|
||||||
ENV PATH="/home/ci/.local/bin:$PATH"
|
|
||||||
|
|
||||||
# Install d2lbook using pip + dependencies for tensorflow d2l
|
|
||||||
RUN pip3 install d2lbook==1.0.0 tensorflow-probability==0.17.0
|
|
||||||
|
|
||||||
# Python script to print framework versions
|
|
||||||
ADD print_versions.py .
|
|
||||||
|
|
||||||
CMD ["/bin/bash"]
|
|
|
@ -1,28 +0,0 @@
|
||||||
# Use PyTorch 1.12 (May 2022)
|
|
||||||
FROM nvcr.io/nvidia/pytorch:22.06-py3
|
|
||||||
|
|
||||||
RUN adduser --disabled-password --disabled-login ci
|
|
||||||
WORKDIR /home/ci
|
|
||||||
|
|
||||||
# Copy d2l_job script
|
|
||||||
ADD d2l_job.sh .
|
|
||||||
RUN chmod +x d2l_job.sh; chown ci d2l_job.sh
|
|
||||||
|
|
||||||
# Copy git timesync for caching
|
|
||||||
ADD git-timesync /home/ci/.local/bin/
|
|
||||||
RUN chmod +x /home/ci/.local/bin/git-timesync
|
|
||||||
|
|
||||||
# Allow permissions for pip installations and git-timesync
|
|
||||||
RUN chown -R ci:ci /home/ci/.local
|
|
||||||
|
|
||||||
USER ci
|
|
||||||
|
|
||||||
ENV PATH="/home/ci/.local/bin:$PATH"
|
|
||||||
|
|
||||||
# Install d2lbook using pip + dependencies for torch d2l
|
|
||||||
RUN pip3 install d2lbook==1.0.0
|
|
||||||
|
|
||||||
# Python script to print framework versions
|
|
||||||
ADD print_versions.py .
|
|
||||||
|
|
||||||
CMD ["/bin/bash"]
|
|
|
@ -1,68 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
date
|
|
||||||
echo "Args: $@"
|
|
||||||
env
|
|
||||||
echo "jobId: $AWS_BATCH_JOB_ID"
|
|
||||||
echo "jobQueue: $AWS_BATCH_JQ_NAME"
|
|
||||||
echo "computeEnvironment: $AWS_BATCH_CE_NAME"
|
|
||||||
|
|
||||||
SOURCE_REF=$1
|
|
||||||
WORK_DIR=$2
|
|
||||||
COMMAND=$3
|
|
||||||
############### NOT USED ATM ##################
|
|
||||||
SAVED_OUTPUT=$4
|
|
||||||
SAVE_PATH=$5
|
|
||||||
###############################################
|
|
||||||
REMOTE=$6
|
|
||||||
SAFE_TO_USE_SCRIPT=$7
|
|
||||||
ORIGINAL_REPO=${8:-'d2l-zh'}
|
|
||||||
# TODO @anirudhdagar: hardcode ORIGINAL_ORG
|
|
||||||
# Avoid ability to change org by restricting
|
|
||||||
# job definition arguments defined in d2l-infra
|
|
||||||
# This is only changed for testing purposes
|
|
||||||
ORIGINAL_ORG=${9:-'d2l-ai'}
|
|
||||||
|
|
||||||
|
|
||||||
# Copy the workflow from master branch
|
|
||||||
git clone https://github.com/"$ORIGINAL_ORG"/"$ORIGINAL_REPO".git
|
|
||||||
|
|
||||||
WORKFLOW_SCRIPTS="$ORIGINAL_REPO"/.github/workflow_scripts
|
|
||||||
if [ -d "$WORKFLOW_SCRIPTS" ]; then
|
|
||||||
cp -R "$ORIGINAL_REPO"/.github/workflow_scripts .
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd "$ORIGINAL_REPO"
|
|
||||||
|
|
||||||
if [ ! -z $REMOTE ]; then
|
|
||||||
git remote set-url origin $REMOTE
|
|
||||||
fi
|
|
||||||
|
|
||||||
git fetch origin $SOURCE_REF:working
|
|
||||||
git checkout working
|
|
||||||
|
|
||||||
# Reset modification times for all notebooks using git-timesync
|
|
||||||
# We use this to make sure d2lbook build eval caching is valid
|
|
||||||
# even after cloning the repo for each run
|
|
||||||
# Modification times for original repo files are corrected and are now
|
|
||||||
# good for comparing with modification times of build files coming
|
|
||||||
# from the S3 bucket
|
|
||||||
git timesync *.md **/*.md
|
|
||||||
|
|
||||||
# If not safe to use script, we overwrite with the script from master branch
|
|
||||||
TRUE=true
|
|
||||||
if [[ ${SAFE_TO_USE_SCRIPT,,} != ${TRUE,,} ]]; then
|
|
||||||
if [ -d ../workflow_scripts ]; then
|
|
||||||
rm -rf .github/workflow_scripts
|
|
||||||
mv ../workflow_scripts .github/
|
|
||||||
else
|
|
||||||
echo Not safe to use user provided script, and could not find script from master branches
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
||||||
cd $WORK_DIR
|
|
||||||
/bin/bash -o pipefail -c "eval $COMMAND"
|
|
||||||
COMMAND_EXIT_CODE=$?
|
|
||||||
|
|
||||||
exit $COMMAND_EXIT_CODE
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
python3 print_versions.py $1
|
|
||||||
/bin/bash
|
|
|
@ -1,288 +0,0 @@
|
||||||
#!/bin/sh
|
|
||||||
|
|
||||||
# This script has been lifted from https://github.com/tst2005/git-timesync/
|
|
||||||
# and all credits for this belong to @tst2005
|
|
||||||
|
|
||||||
# shellcheck disable=SC2004,SC3043,SC2155,SC2039
|
|
||||||
|
|
||||||
#### About shellcheck
|
|
||||||
# I disable:
|
|
||||||
# - SC2004 (style): $/${} is unnecessary on arithmetic variables.
|
|
||||||
# Because: I prefere `$( $x - $y ))` than `$(( x - y ))`.
|
|
||||||
|
|
||||||
# I disable:
|
|
||||||
# - SC2039: In POSIX sh, 'local' is undefined.
|
|
||||||
# - SC3043: In POSIX sh, 'local' is undefined.
|
|
||||||
# Because:
|
|
||||||
# local is too usefull, all modern shell support it.
|
|
||||||
# If you really want to run git-timesync on a strict POSIX shell,
|
|
||||||
# then remove all local prefixes : 's/local //g'.
|
|
||||||
#
|
|
||||||
# I disable :
|
|
||||||
# - SC2155 (warning): Declare and assign separately to avoid masking return values.
|
|
||||||
# Because:
|
|
||||||
# It not relevant : the return code is not used.
|
|
||||||
# I prefer `local foo="$(bar)"` than `local foo;foo="$(bar)"`.
|
|
||||||
####
|
|
||||||
|
|
||||||
set -e
|
|
||||||
|
|
||||||
####
|
|
||||||
# Author: TsT worldmaster.fr <tst2005@gmail.com>
|
|
||||||
#
|
|
||||||
# Improvements:
|
|
||||||
# - dry-run ("-n" flag)
|
|
||||||
# - pass files to check as argument
|
|
||||||
# - do not time sync modified files
|
|
||||||
# - do not time sync untracked files
|
|
||||||
# - performance improvment
|
|
||||||
# - be able to apply timesync only on files present in the N last commits
|
|
||||||
####
|
|
||||||
|
|
||||||
####
|
|
||||||
# The original version of this script can be found at: https://gist.github.com/jeffery/1115504
|
|
||||||
#
|
|
||||||
# Helper script to update the Last modified timestamp of files in a Git SCM
|
|
||||||
# Projects working Copy
|
|
||||||
#
|
|
||||||
# When you clone a Git repository, it sets the timestamp of all the files to the
|
|
||||||
# time when you cloned the repository.
|
|
||||||
#
|
|
||||||
# This becomes a problem when you want the cloned repository, which is part of a
|
|
||||||
# Web application have a proper cacheing mechanism so that it can re-cache files
|
|
||||||
# (into a webtree) that have been modified since the last cache.
|
|
||||||
#
|
|
||||||
# @see http://stackoverflow.com/questions/1964470/whats-the-equivalent-of-use-commit-times-for-git
|
|
||||||
#
|
|
||||||
# Author: Jeffery Fernandez <jeffery@fernandez.net.au>
|
|
||||||
####
|
|
||||||
|
|
||||||
showUsage() {
|
|
||||||
echo 'Usage: git-timesync [-n] [-q] [-v] [--] [<paths>...]'
|
|
||||||
echo 'Usage: git-timesync [-n] [-q] [-v] -<N>'
|
|
||||||
echo
|
|
||||||
echo ' -h, --help '\
|
|
||||||
'Print this help message'
|
|
||||||
echo ' -n, --dry-run, --dryrun '\
|
|
||||||
'Perform a dry-run to see which files are OK'\
|
|
||||||
'and which ones need to be synchronized'
|
|
||||||
echo ' -q, --quiet '\
|
|
||||||
'Quiet mode: drop everything that is OK and show only the files'\
|
|
||||||
'which timestamp needs to be synchronized'
|
|
||||||
echo ' -v, --verbose '\
|
|
||||||
'Verbose mode: show info for each file (opposite of --quiet)'
|
|
||||||
echo ' -1 '\
|
|
||||||
'Apply timesync on files present in the last commit'
|
|
||||||
echo ' -23 '\
|
|
||||||
'Apply timesync on files present in the 23 last commits'
|
|
||||||
echo ' -N '\
|
|
||||||
'Apply timesync on files present in the N last commits'\
|
|
||||||
'(with 1 <= N <= 9999)'
|
|
||||||
}
|
|
||||||
|
|
||||||
# Get the last revision hash of a particular file in the git repository
|
|
||||||
getFileLastRevision() {
|
|
||||||
git rev-list HEAD -n 1 -- "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
#getFileMTimeByRef() {
|
|
||||||
# git show --pretty=format:%at --abbrev-commit "$1" | head -n 1
|
|
||||||
#}
|
|
||||||
|
|
||||||
getFileMTimeByPath() {
|
|
||||||
# shellcheck disable=SC2155
|
|
||||||
git rev-list --pretty=format:'date %at' --date-order -n 1 HEAD -- "$1" |
|
|
||||||
(
|
|
||||||
local IFS=" ";
|
|
||||||
# shellcheck disable=SC2034
|
|
||||||
while read -r key value _misc; do
|
|
||||||
[ "$key" != "date" ] || echo "$value";
|
|
||||||
done
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
# Extract the actual last modified timestamp of the file and Update the timestamp
|
|
||||||
updateFileTimeStamp() {
|
|
||||||
# shellcheck disable=SC2155
|
|
||||||
|
|
||||||
# if target does not exists and it's is not a [dead]link, raise an error
|
|
||||||
if [ ! -e "$1" ] && [ ! -h "$1" ]; then
|
|
||||||
if [ -n "$(git ls-files -t -d -- "$1")" ]; then
|
|
||||||
if $verbose; then echo "? $1 (deleted)"; fi
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
echo >&2 "ERROR: Unknown bug ?! No such target $1"
|
|
||||||
return 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
local tracked="$(git ls-files -t -c -- "$1")"
|
|
||||||
if [ -z "$tracked" ]; then
|
|
||||||
if $verbose; then echo "? $1"; fi
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Extract the last modified timestamp
|
|
||||||
# Get the File last modified time
|
|
||||||
local FILE_MODIFIED_TIME="$(getFileMTimeByPath "$1")"
|
|
||||||
if [ -z "$FILE_MODIFIED_TIME" ]; then
|
|
||||||
echo "?! $1 (not found in git)"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Check if the file is modified
|
|
||||||
local uncommited="$(git ls-files -t -dm -- "$1")"
|
|
||||||
|
|
||||||
# for displaying the date in readable format
|
|
||||||
#local FORMATTED_TIMESTAMP="$(date --date="${FILE_MODIFIED_TIME}" +'%d-%m-%Y %H:%M:%S %z')"
|
|
||||||
#local FORMATTED_TIMESTAMP="@${FILE_MODIFIED_TIME}"
|
|
||||||
|
|
||||||
# Modify the last modified timestamp
|
|
||||||
#echo "[$(date -d "$FORMATTED_TIMESTAMP")]: $1"
|
|
||||||
#echo "$FILE_MODIFIED_TIME $1"
|
|
||||||
local current_mtime="$(getmtime "$1")"
|
|
||||||
if $debug; then
|
|
||||||
echo >&2 "DEBUG: $1 (git_time=$FILE_MODIFIED_TIME current_time=$current_mtime delta=$(( ${current_mtime:-0} - ${FILE_MODIFIED_TIME:-0} )))"
|
|
||||||
fi
|
|
||||||
if [ "$current_mtime" = "$FILE_MODIFIED_TIME" ]; then
|
|
||||||
if ${verbose:-true}; then echo "ok $1"; fi
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
if [ -n "$uncommited" ]; then
|
|
||||||
echo "C $1 (modified, not commited, $(( $current_mtime - $FILE_MODIFIED_TIME ))s recent)"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
if ${dryrun:-true}; then
|
|
||||||
echo "!! $1 (desync: $(( $current_mtime - $FILE_MODIFIED_TIME ))s, no change)"
|
|
||||||
return
|
|
||||||
fi
|
|
||||||
echo "!! $1 (desync: $(( $current_mtime - $FILE_MODIFIED_TIME ))s, syncing...)"
|
|
||||||
#[ -h "$1" ] && touch -c -h -d "$FORMATTED_TIMESTAMP" -- "$1" || \
|
|
||||||
#touch -c -d "$FORMATTED_TIMESTAMP" -- "$1"
|
|
||||||
unixtime_touch -c -h -- "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
# Make sure we are not running this on a bare Repository
|
|
||||||
is_not_base_repo() {
|
|
||||||
case "$(git config core.bare)" in
|
|
||||||
false) ;;
|
|
||||||
true)
|
|
||||||
echo "$(pwd): Cannot run this script on a bare Repository"
|
|
||||||
return 1
|
|
||||||
;;
|
|
||||||
*) echo "$(pwd): Error appended during core.bare detection. Are you really inside a repository ?"
|
|
||||||
return 1
|
|
||||||
esac
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
|
|
||||||
updateFileTimeStampInCwd() {
|
|
||||||
is_not_base_repo || return
|
|
||||||
|
|
||||||
git ls-files -z \
|
|
||||||
| tr '\0' '\n' \
|
|
||||||
| (
|
|
||||||
while read -r file; do
|
|
||||||
if [ -z "$(git ls-files -t -d -- "$file")" ]; then
|
|
||||||
updateFileTimeStamp "${file}"
|
|
||||||
fi
|
|
||||||
done
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
timesyncThisFile() {
|
|
||||||
if [ -d "$1" ] && [ ! -h "$1" ]; then # is a real directory (not a symlink to a directory)
|
|
||||||
echo "now inside $1"
|
|
||||||
# shellcheck disable=SC2015
|
|
||||||
( cd -- "$1" && updateFileTimeStampInCwd || true; )
|
|
||||||
else
|
|
||||||
if $need_check_bare; then
|
|
||||||
is_not_base_repo || return 1
|
|
||||||
need_check_bare=false
|
|
||||||
fi
|
|
||||||
updateFileTimeStamp "$1"
|
|
||||||
fi
|
|
||||||
}
|
|
||||||
|
|
||||||
# ... for Linux ... and MINGW64 (used by Windows GIT Bash)
|
|
||||||
linux_unixtime_touch() {
|
|
||||||
# shellcheck disable=SC2155
|
|
||||||
local FORMATTED_TIMESTAMP="@${FILE_MODIFIED_TIME}"
|
|
||||||
touch -d "$FORMATTED_TIMESTAMP" "$@"
|
|
||||||
}
|
|
||||||
linux_getmtime() {
|
|
||||||
stat -c %Y -- "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
# ... for FreeBSD and Mac OS X
|
|
||||||
bsd_unixtime_touch() {
|
|
||||||
# shellcheck disable=SC2155
|
|
||||||
local FORMATTED_TIMESTAMP="$(date -j -r "${FILE_MODIFIED_TIME}" +'%Y%m%d%H%M.%S')"
|
|
||||||
touch -t "$FORMATTED_TIMESTAMP" "$@"
|
|
||||||
}
|
|
||||||
bsd_getmtime() {
|
|
||||||
stat -f %m -- "$1"
|
|
||||||
}
|
|
||||||
|
|
||||||
################################################################################
|
|
||||||
############################## MAIN SCRIPT LOGIC ###############################
|
|
||||||
################################################################################
|
|
||||||
|
|
||||||
dryrun=false
|
|
||||||
verbose=true
|
|
||||||
debug=false
|
|
||||||
fromrecent=''
|
|
||||||
while [ $# -gt 0 ]; do
|
|
||||||
case "$1" in
|
|
||||||
--) shift; break ;;
|
|
||||||
-h|--help) showUsage; exit 0;;
|
|
||||||
-n|--dryrun|--dry-run) dryrun=true ;;
|
|
||||||
-v) verbose=true ;;
|
|
||||||
-q) verbose=false ;;
|
|
||||||
-[1-9]|-[1-9][0-9]|-[1-9][0-9][0-9]|-[1-9][0-9][0-9][0-9]) fromrecent="$1" ;;
|
|
||||||
--debug) debug=true ;;
|
|
||||||
-*) echo >&2 "$0: invalid option $1"; exit 1;;
|
|
||||||
*) break
|
|
||||||
esac
|
|
||||||
shift
|
|
||||||
done
|
|
||||||
|
|
||||||
# Obtain the Operating System
|
|
||||||
case "${GIT_TIMESYNC_FORCE_UNAME:-$(uname)}" in
|
|
||||||
('Linux'|'MINGW64'*)
|
|
||||||
unixtime_touch() { linux_unixtime_touch "$@"; }
|
|
||||||
getmtime() { linux_getmtime "$@"; }
|
|
||||||
;;
|
|
||||||
('Darwin'|'FreeBSD')
|
|
||||||
unixtime_touch() { bsd_unixtime_touch "$@"; }
|
|
||||||
getmtime() { bsd_getmtime "$@"; }
|
|
||||||
;;
|
|
||||||
(*)
|
|
||||||
echo >&2 "Unknown Operating System to perform timestamp update"
|
|
||||||
exit 1
|
|
||||||
;;
|
|
||||||
esac
|
|
||||||
|
|
||||||
if [ $# -eq 0 ] && [ -z "$fromrecent" ]; then
|
|
||||||
# Loop through and fix timestamps on all files in our checked-out repository
|
|
||||||
updateFileTimeStampInCwd
|
|
||||||
else
|
|
||||||
need_check_bare=true
|
|
||||||
|
|
||||||
# Loop through and fix timestamps on all specified files
|
|
||||||
if [ -n "$fromrecent" ]; then
|
|
||||||
git log --format='format:' --name-only "$fromrecent" |
|
|
||||||
sort -u |
|
|
||||||
while read -r file; do
|
|
||||||
[ -n "$file" ] || continue
|
|
||||||
[ -e "$file" ] || continue
|
|
||||||
timesyncThisFile "$file"
|
|
||||||
done
|
|
||||||
else
|
|
||||||
for file in "$@"; do
|
|
||||||
timesyncThisFile "$file"
|
|
||||||
done
|
|
||||||
fi
|
|
||||||
fi
|
|
||||||
|
|
|
@ -1,3 +0,0 @@
|
||||||
#!/bin/bash
|
|
||||||
|
|
||||||
aws ecr get-login-password --region us-west-2 | docker login --username AWS --password-stdin 650140442593.dkr.ecr.us-west-2.amazonaws.com
|
|
|
@ -1,51 +0,0 @@
|
||||||
import os
|
|
||||||
import sys
|
|
||||||
|
|
||||||
if len(sys.argv) > 1:
|
|
||||||
framework_name = sys.argv[1]
|
|
||||||
else:
|
|
||||||
# Assume using d2l-builder docker container
|
|
||||||
# Here all the frameworks are installed and no CUDA support
|
|
||||||
framework_name = None
|
|
||||||
|
|
||||||
print("*"*10, "D2L Framework Version Details", "*"*10)
|
|
||||||
|
|
||||||
if framework_name:
|
|
||||||
# Print CUDA version
|
|
||||||
print("nvcc --version")
|
|
||||||
print(os.system("nvcc --version"))
|
|
||||||
|
|
||||||
if framework_name=="pytorch":
|
|
||||||
# Print PyTorch versions
|
|
||||||
print(f"Framework Name: {framework_name}")
|
|
||||||
import torch; print(f"torch version: {torch.__version__}")
|
|
||||||
import torchvision; print(f"torchvision version: {torchvision.__version__}")
|
|
||||||
import gym; print(f"gym version: {gym.__version__}")
|
|
||||||
import gpytorch; print(f"gpytorch version: {gpytorch.__version__}")
|
|
||||||
import syne_tune; print(f"syne_tune version: {syne_tune.__version__}")
|
|
||||||
|
|
||||||
|
|
||||||
if framework_name=="tensorflow":
|
|
||||||
# Print TensorFlow versions
|
|
||||||
print(f"Framework Name: {framework_name}")
|
|
||||||
import tensorflow; print(f"tensorflow version: {tensorflow.__version__}")
|
|
||||||
import tensorflow_probability; print(f"tensorflow_probability version: {tensorflow_probability.__version__}")
|
|
||||||
|
|
||||||
if framework_name=="jax":
|
|
||||||
# Print JAX versions
|
|
||||||
print(f"Framework Name: {framework_name}")
|
|
||||||
import jax; print(f"jax version: {jax.__version__}")
|
|
||||||
import jaxlib; print(f"jaxlib version: {jaxlib.__version__}")
|
|
||||||
import flax; print(f"flax version: {flax.__version__}")
|
|
||||||
import tensorflow_datasets; print(f"tensorflow_datasets version: {tensorflow_datasets.__version__}")
|
|
||||||
|
|
||||||
if framework_name=="mxnet":
|
|
||||||
# Print MXNet versions
|
|
||||||
print(f"Framework Name: {framework_name}")
|
|
||||||
import mxnet; print(f"MXNet version: {mxnet.__version__}")
|
|
||||||
|
|
||||||
|
|
||||||
# Print d2lbook version
|
|
||||||
import d2lbook; print(f"d2lbook version: {d2lbook.__version__}")
|
|
||||||
|
|
||||||
print("*"*10, "D2L Framework Version Details", "*"*10)
|
|
202
ci/submit-job.py
|
@ -1,202 +0,0 @@
|
||||||
import argparse
|
|
||||||
import random
|
|
||||||
import os
|
|
||||||
import re
|
|
||||||
import sys
|
|
||||||
import time
|
|
||||||
from datetime import datetime
|
|
||||||
|
|
||||||
import boto3
|
|
||||||
from botocore.compat import total_seconds
|
|
||||||
from botocore.config import Config
|
|
||||||
|
|
||||||
|
|
||||||
job_type_info = {
|
|
||||||
'ci-cpu': {
|
|
||||||
'job_definition': 'd2l-ci-cpu-builder:2',
|
|
||||||
'job_queue': 'D2L-CI-CPU'
|
|
||||||
},
|
|
||||||
'ci-cpu-push': {
|
|
||||||
'job_definition': 'd2l-ci-cpu-builder-push:7',
|
|
||||||
'job_queue': 'D2L-CI-CPU'
|
|
||||||
},
|
|
||||||
'ci-cpu-release': {
|
|
||||||
'job_definition': 'd2l-ci-cpu-builder-release:1',
|
|
||||||
'job_queue': 'D2L-CI-CPU'
|
|
||||||
},
|
|
||||||
'ci-gpu-torch': {
|
|
||||||
'job_definition': 'd2l-ci-zh-gpu-torch:1',
|
|
||||||
'job_queue': 'D2L-CI-GPU'
|
|
||||||
},
|
|
||||||
'ci-gpu-tf': {
|
|
||||||
'job_definition': 'd2l-ci-zh-gpu-tf:1',
|
|
||||||
'job_queue': 'D2L-CI-GPU'
|
|
||||||
},
|
|
||||||
'ci-gpu-mxnet': {
|
|
||||||
'job_definition': 'd2l-ci-zh-gpu-mxnet:1',
|
|
||||||
'job_queue': 'D2L-CI-GPU'
|
|
||||||
},
|
|
||||||
'ci-gpu-paddle': {
|
|
||||||
'job_definition': 'd2l-ci-zh-gpu-paddle:1',
|
|
||||||
'job_queue': 'D2L-CI-GPU'
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
# Create push job types for GPUs with same definitions
|
|
||||||
for job_type in list(job_type_info.keys()):
|
|
||||||
if job_type.startswith('ci-gpu'):
|
|
||||||
job_type_info[job_type+'-push'] = job_type_info[job_type]
|
|
||||||
job_type_info[job_type+'-release'] = job_type_info[job_type]
|
|
||||||
|
|
||||||
parser = argparse.ArgumentParser(formatter_class=argparse.ArgumentDefaultsHelpFormatter)
|
|
||||||
|
|
||||||
parser.add_argument('--profile', help='profile name of aws account.', type=str,
|
|
||||||
default=None)
|
|
||||||
parser.add_argument('--region', help='Default region when creating new connections', type=str,
|
|
||||||
default='us-west-2')
|
|
||||||
parser.add_argument('--name', help='name of the job', type=str, default='d2l-ci')
|
|
||||||
parser.add_argument('--job-type', help='type of job to submit.', type=str,
|
|
||||||
choices=job_type_info.keys(), default='ci-cpu')
|
|
||||||
parser.add_argument('--source-ref',
|
|
||||||
help='ref in d2l-zh main github. e.g. master, refs/pull/500/head',
|
|
||||||
type=str, default='master')
|
|
||||||
parser.add_argument('--work-dir',
|
|
||||||
help='working directory inside the repo. e.g. scripts/preprocess',
|
|
||||||
type=str, default='.')
|
|
||||||
parser.add_argument('--saved-output',
|
|
||||||
help='output to be saved, relative to working directory. '
|
|
||||||
'it can be either a single file or a directory',
|
|
||||||
type=str, default='None')
|
|
||||||
parser.add_argument('--save-path',
|
|
||||||
help='s3 path where files are saved.',
|
|
||||||
type=str, default='batch/temp/{}'.format(datetime.now().isoformat()))
|
|
||||||
parser.add_argument('--command', help='command to run', type=str,
|
|
||||||
default='git rev-parse HEAD | tee stdout.log')
|
|
||||||
parser.add_argument('--remote',
|
|
||||||
help='git repo address. https://github.com/d2l-ai/d2l-zh',
|
|
||||||
type=str, default="https://github.com/d2l-ai/d2l-zh")
|
|
||||||
parser.add_argument('--safe-to-use-script',
|
|
||||||
help='whether the script changes from the actor is safe. We assume it is safe if the actor has write permission to our repo',
|
|
||||||
action='store_true')
|
|
||||||
parser.add_argument('--original-repo', help='name of the repo', type=str, default='d2l-zh')
|
|
||||||
parser.add_argument('--wait', help='block wait until the job completes. '
|
|
||||||
'Non-zero exit code if job fails.', action='store_true')
|
|
||||||
parser.add_argument('--timeout', help='job timeout in seconds', default=7200, type=int)
|
|
||||||
|
|
||||||
|
|
||||||
args = parser.parse_args()
|
|
||||||
|
|
||||||
session = boto3.Session(profile_name=args.profile, region_name=args.region)
|
|
||||||
config = Config(
|
|
||||||
retries = dict(
|
|
||||||
max_attempts = 20
|
|
||||||
)
|
|
||||||
)
|
|
||||||
batch, cloudwatch = [session.client(service_name=sn, config=config) for sn in ['batch', 'logs']]
|
|
||||||
|
|
||||||
|
|
||||||
def printLogs(logGroupName, logStreamName, startTime):
|
|
||||||
kwargs = {'logGroupName': logGroupName,
|
|
||||||
'logStreamName': logStreamName,
|
|
||||||
'startTime': startTime,
|
|
||||||
'startFromHead': True}
|
|
||||||
|
|
||||||
lastTimestamp = startTime - 1
|
|
||||||
while True:
|
|
||||||
logEvents = cloudwatch.get_log_events(**kwargs)
|
|
||||||
|
|
||||||
for event in logEvents['events']:
|
|
||||||
lastTimestamp = event['timestamp']
|
|
||||||
timestamp = datetime.utcfromtimestamp(lastTimestamp / 1000.0).isoformat()
|
|
||||||
print('[{}] {}'.format((timestamp + '.000')[:23] + 'Z', event['message']))
|
|
||||||
|
|
||||||
nextToken = logEvents['nextForwardToken']
|
|
||||||
if nextToken and kwargs.get('nextToken') != nextToken:
|
|
||||||
kwargs['nextToken'] = nextToken
|
|
||||||
else:
|
|
||||||
break
|
|
||||||
return lastTimestamp
|
|
||||||
|
|
||||||
|
|
||||||
def nowInMillis():
|
|
||||||
endTime = int(total_seconds(datetime.utcnow() - datetime(1970, 1, 1))) * 1000
|
|
||||||
return endTime
|
|
||||||
|
|
||||||
|
|
||||||
def main():
|
|
||||||
spin = ['-', '/', '|', '\\', '-', '/', '|', '\\']
|
|
||||||
logGroupName = '/aws/batch/job'
|
|
||||||
|
|
||||||
jobName = re.sub('[^A-Za-z0-9_\-]', '', args.name)[:128] # Enforce AWS Batch jobName rules
|
|
||||||
jobType = args.job_type
|
|
||||||
jobQueue = job_type_info[jobType]['job_queue']
|
|
||||||
jobDefinition = job_type_info[jobType]['job_definition']
|
|
||||||
wait = args.wait
|
|
||||||
|
|
||||||
safe_to_use_script = 'False'
|
|
||||||
if args.safe_to_use_script:
|
|
||||||
safe_to_use_script = 'True'
|
|
||||||
|
|
||||||
parameters = {
|
|
||||||
'SOURCE_REF': args.source_ref,
|
|
||||||
'WORK_DIR': args.work_dir,
|
|
||||||
'SAVED_OUTPUT': args.saved_output,
|
|
||||||
'SAVE_PATH': args.save_path,
|
|
||||||
'COMMAND': f"\"{args.command}\"", # wrap command with double quotation mark, so that batch can treat it as a single command
|
|
||||||
'REMOTE': args.remote,
|
|
||||||
'SAFE_TO_USE_SCRIPT': safe_to_use_script,
|
|
||||||
'ORIGINAL_REPO': args.original_repo
|
|
||||||
}
|
|
||||||
kwargs = dict(
|
|
||||||
jobName=jobName,
|
|
||||||
jobQueue=jobQueue,
|
|
||||||
jobDefinition=jobDefinition,
|
|
||||||
parameters=parameters,
|
|
||||||
)
|
|
||||||
if args.timeout is not None:
|
|
||||||
kwargs['timeout'] = {'attemptDurationSeconds': args.timeout}
|
|
||||||
submitJobResponse = batch.submit_job(**kwargs)
|
|
||||||
|
|
||||||
jobId = submitJobResponse['jobId']
|
|
||||||
|
|
||||||
# Export Batch_JobID to Github Actions Environment Variable
|
|
||||||
with open(os.environ['GITHUB_ENV'], 'a') as f:
|
|
||||||
f.write(f'Batch_JobID={jobId}\n')
|
|
||||||
os.environ['batch_jobid'] = jobId
|
|
||||||
|
|
||||||
print('Submitted job [{} - {}] to the job queue [{}]'.format(jobName, jobId, jobQueue))
|
|
||||||
|
|
||||||
spinner = 0
|
|
||||||
running = False
|
|
||||||
status_set = set()
|
|
||||||
startTime = 0
|
|
||||||
logStreamName = None
|
|
||||||
while wait:
|
|
||||||
time.sleep(random.randint(5, 10))
|
|
||||||
describeJobsResponse = batch.describe_jobs(jobs=[jobId])
|
|
||||||
status = describeJobsResponse['jobs'][0]['status']
|
|
||||||
if status == 'SUCCEEDED' or status == 'FAILED':
|
|
||||||
if logStreamName:
|
|
||||||
startTime = printLogs(logGroupName, logStreamName, startTime) + 1
|
|
||||||
print('=' * 80)
|
|
||||||
print('Job [{} - {}] {}'.format(jobName, jobId, status))
|
|
||||||
sys.exit(status == 'FAILED')
|
|
||||||
|
|
||||||
elif status == 'RUNNING':
|
|
||||||
logStreamName = describeJobsResponse['jobs'][0]['container']['logStreamName']
|
|
||||||
if not running:
|
|
||||||
running = True
|
|
||||||
print('\rJob [{}, {}] is RUNNING.'.format(jobName, jobId))
|
|
||||||
if logStreamName:
|
|
||||||
print('Output [{}]:\n {}'.format(logStreamName, '=' * 80))
|
|
||||||
if logStreamName:
|
|
||||||
startTime = printLogs(logGroupName, logStreamName, startTime) + 1
|
|
||||||
elif status not in status_set:
|
|
||||||
status_set.add(status)
|
|
||||||
print('\rJob [%s - %s] is %-9s... %s' % (jobName, jobId, status, spin[spinner % len(spin)]),)
|
|
||||||
sys.stdout.flush()
|
|
||||||
spinner += 1
|
|
||||||
|
|
||||||
|
|
||||||
if __name__ == '__main__':
|
|
||||||
main()
|
|
|
@ -28,8 +28,8 @@ def read_data_ml100k():
|
||||||
"""读取MovieLens-100k数据集"""
|
"""读取MovieLens-100k数据集"""
|
||||||
data_dir = d2l.download_extract('ml-100k')
|
data_dir = d2l.download_extract('ml-100k')
|
||||||
names = ['user_id', 'item_id', 'rating', 'timestamp']
|
names = ['user_id', 'item_id', 'rating', 'timestamp']
|
||||||
data = pd.read_csv(os.path.join(data_dir, 'u.data'), sep='\t',
|
data = pd.read_csv(os.path.join(data_dir, 'u.data'), '\t', names=names,
|
||||||
names=names, engine='python')
|
engine='python')
|
||||||
num_users = data.user_id.unique().shape[0]
|
num_users = data.user_id.unique().shape[0]
|
||||||
num_items = data.item_id.unique().shape[0]
|
num_items = data.item_id.unique().shape[0]
|
||||||
return data, num_users, num_items
|
return data, num_users, num_items
|
||||||
|
|
1009
d2l/mxnet.py
795
d2l/torch.py
Before Width: | Height: | Size: 619 KiB After Width: | Height: | Size: 619 KiB |
Before Width: | Height: | Size: 599 KiB After Width: | Height: | Size: 599 KiB |
Before Width: | Height: | Size: 718 KiB |
Before Width: | Height: | Size: 615 KiB |
Before Width: | Height: | Size: 999 KiB |
Before Width: | Height: | Size: 1.9 MiB |
Before Width: | Height: | Size: 436 KiB |
Before Width: | Height: | Size: 8.1 MiB |
Before Width: | Height: | Size: 2.5 MiB |
Before Width: | Height: | Size: 1020 KiB |
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 2.7 MiB |
Before Width: | Height: | Size: 2.7 MiB |
Before Width: | Height: | Size: 441 KiB |
Before Width: | Height: | Size: 570 KiB |
Before Width: | Height: | Size: 2.5 MiB |
Before Width: | Height: | Size: 704 KiB |
Before Width: | Height: | Size: 646 KiB |
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 574 KiB |
Before Width: | Height: | Size: 744 KiB |
Before Width: | Height: | Size: 1.0 MiB |
Before Width: | Height: | Size: 460 KiB |
|
@ -203,7 +203,7 @@ a {
|
||||||
<p class="version"> 跳转<a href="https://zh-v1.d2l.ai/">第一版</a></p>
|
<p class="version"> 跳转<a href="https://zh-v1.d2l.ai/">第一版</a></p>
|
||||||
<p>面向中文读者的能运行、可讨论的深度学习教科书</p>
|
<p>面向中文读者的能运行、可讨论的深度学习教科书</p>
|
||||||
<p>含 PyTorch、NumPy/MXNet、TensorFlow 和 PaddlePaddle 实现</p>
|
<p>含 PyTorch、NumPy/MXNet、TensorFlow 和 PaddlePaddle 实现</p>
|
||||||
<p>被全球 70 多个国家 500 多所大学用于教学</p>
|
<p>被全球 60 多个国家 400 多所大学用于教学</p>
|
||||||
<p><a class="github-button" href="https://github.com/d2l-ai/d2l-zh" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star d2l-ai/d2l-zh on GitHub">Star</a></p>
|
<p><a class="github-button" href="https://github.com/d2l-ai/d2l-zh" data-icon="octicon-star" data-size="large" data-show-count="true" aria-label="Star d2l-ai/d2l-zh on GitHub">Star</a></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -213,18 +213,13 @@ a {
|
||||||
<h3>公告</h3>
|
<h3>公告</h3>
|
||||||
<div align="left">
|
<div align="left">
|
||||||
<ul>
|
<ul>
|
||||||
<li><b>【重磅升级,<a href="https://raw.githubusercontent.com/d2l-ai/d2l-zh/master/static/frontpage/_images/sales/jd-20230208-zh-6.png">新书榜第一</a>】</b>
|
<li><b>【重磅升级】</b>
|
||||||
第二版纸质书——《动手学深度学习(PyTorch版)》(黑白平装版)
|
第二版纸质书——《动手学深度学习(PyTorch版)》预计于2023年元旦面世。第二版在线内容新增PaddlePaddle实现。
|
||||||
已在
|
|
||||||
<a href="https://item.jd.com/13628339.html">京东</a>、
|
|
||||||
<a href="https://product.dangdang.com/29511471.html">当当</a>
|
|
||||||
上架。
|
|
||||||
纸质书在内容上与在线版大致相同,但力求在样式、术语标注、语言表述、用词规范、标点以及图、表、章节的索引上符合出版标准和学术规范。
|
|
||||||
第二版在线内容新增PaddlePaddle实现。
|
|
||||||
关注本书的<a href="https://github.com/d2l-ai/d2l-zh">中文开源项目</a>和<a href="https://github.com/d2l-ai/d2l-en">英文开源项目</a>以及时获取最新信息。
|
关注本书的<a href="https://github.com/d2l-ai/d2l-zh">中文开源项目</a>和<a href="https://github.com/d2l-ai/d2l-en">英文开源项目</a>以及时获取最新信息。
|
||||||
</li>
|
</li>
|
||||||
<li><b>【第一版纸质书】</b>
|
<li><b>【购买第一版纸质书(上架4周重印2次,累计3万+册)】</b>
|
||||||
可在
|
纸质书在内容上与在线版大致相同,但力求在样式、术语标注、语言表述、用词规范、标点以及图、表、章节的索引上符合出版标准和学术规范。
|
||||||
|
可以在
|
||||||
<a href="https://item.jd.com/12613094.html">京东</a>、
|
<a href="https://item.jd.com/12613094.html">京东</a>、
|
||||||
<a href="http://product.dangdang.com/27872783.html">当当</a>、
|
<a href="http://product.dangdang.com/27872783.html">当当</a>、
|
||||||
<a href="https://detail.tmall.com/item.htm?id=594937167055">天猫</a>
|
<a href="https://detail.tmall.com/item.htm?id=594937167055">天猫</a>
|
||||||
|
@ -237,7 +232,8 @@ a {
|
||||||
[<a href="https://raw.githubusercontent.com/d2l-ai/d2l-zh/v1/img/frontpage/jd-190715-zh.png">新书榜</a>]
|
[<a href="https://raw.githubusercontent.com/d2l-ai/d2l-zh/v1/img/frontpage/jd-190715-zh.png">新书榜</a>]
|
||||||
[<a href="https://zhuanlan.zhihu.com/p/66689123">关于样书</a>]
|
[<a href="https://zhuanlan.zhihu.com/p/66689123">关于样书</a>]
|
||||||
</li>
|
</li>
|
||||||
<li><b>【免费资源】</b>
|
<li><b>【免费资源(新增中文版课件)】</b>
|
||||||
|
在校学生和老师可以申请用于本书学习或教学的<a href="http://zh.d2l.ai/aws4learn.html">免费计算资源</a>。
|
||||||
课件、作业、教学视频等资源可参考伯克利“深度学习导论”
|
课件、作业、教学视频等资源可参考伯克利“深度学习导论”
|
||||||
<a href="https://courses.d2l.ai/berkeley-stat-157/syllabus.html">课程大纲</a>
|
<a href="https://courses.d2l.ai/berkeley-stat-157/syllabus.html">课程大纲</a>
|
||||||
中的链接(<a href="https://github.com/d2l-ai/berkeley-stat-157/tree/master/slides-zh">中文版课件</a>)。
|
中的链接(<a href="https://github.com/d2l-ai/berkeley-stat-157/tree/master/slides-zh">中文版课件</a>)。
|
||||||
|
@ -261,28 +257,28 @@ a {
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/aston.jpg"/>
|
<img src="./_images/aston.jpg"/>
|
||||||
<h3><a href="https://astonzhang.github.io/">阿斯顿·张 </a></h3>
|
<h3><a href="https://astonzhang.github.io/">阿斯顿·张 </a></h3>
|
||||||
<p>亚马逊</p>
|
<p>亚马逊资深科学家</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/zack.jpg"/>
|
<img src="./_images/zack.jpg"/>
|
||||||
<h3><a href="http://zacklipton.com/">扎卡里 C. 立顿</a></h3>
|
<h3><a href="http://zacklipton.com/">扎卡里 C. 立顿</a></h3>
|
||||||
<p> 美国卡内基梅隆大学、亚马逊</p>
|
<p> 亚马逊科学家,美国卡内基梅隆大学助理教授</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/mu.jpg"/>
|
<img src="./_images/mu.jpg"/>
|
||||||
<h3><a href="http://www.cs.cmu.edu/~muli/">李沐</a></h3>
|
<h3><a href="http://www.cs.cmu.edu/~muli/">李沐</a></h3>
|
||||||
<p>亚马逊</p>
|
<p>亚马逊资深首席科学家</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/alex.jpg"/>
|
<img src="./_images/alex.jpg"/>
|
||||||
<h3><a href="https://alex.smola.org/">亚历山大 J. 斯莫拉</a></h3>
|
<h3><a href="https://alex.smola.org/">亚历山大 J. 斯莫拉</a></h3>
|
||||||
<p>亚马逊</p>
|
<p>亚马逊副总裁/杰出科学家</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -293,21 +289,21 @@ a {
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/brent.jpg"/>
|
<img src="./_images/brent.jpg"/>
|
||||||
<h3><a href="https://www.linkedin.com/in/brent-werness-1506471b7/">布伦特 沃尼斯</a></h3>
|
<h3><a href="https://www.linkedin.com/in/brent-werness-1506471b7/">布伦特 沃尼斯</a></h3>
|
||||||
<p>亚马逊<br><i><a href="http://d2l.ai/chapter_appendix-mathematics-for-deep-learning/index.html">深度学习的数学</a></i></p>
|
<p>亚马逊资深科学家<br><i><a href="http://d2l.ai/chapter_appendix-mathematics-for-deep-learning/index.html">深度学习的数学</a></i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/rachel.jpeg"/>
|
<img src="./_images/rachel.jpeg"/>
|
||||||
<h3><a href="https://www.linkedin.com/in/rachelsonghu/">瑞潮儿·胡</a></h3>
|
<h3><a href="https://www.linkedin.com/in/rachelsonghu/">瑞潮儿·胡</a></h3>
|
||||||
<p> 亚马逊<br><i><a href="http://d2l.ai/chapter_appendix-mathematics-for-deep-learning/index.html">深度学习的数学</a></i></p>
|
<p> 亚马逊科学家<br><i><a href="http://d2l.ai/chapter_appendix-mathematics-for-deep-learning/index.html">深度学习的数学</a></i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/shuai.jpg"/>
|
<img src="./_images/shuai.jpg"/>
|
||||||
<h3><a href="https://shuaizhang.tech/">张帅</a></h3>
|
<h3><a href="https://shuaizhang.tech/">张帅</a></h3>
|
||||||
<p>亚马逊
|
<p>苏黎世联邦理工学院博士后
|
||||||
<br><i><a href="http://d2l.ai/chapter_recommender-systems/index.html">推荐系统</a></i></p>
|
<br><i><a href="http://d2l.ai/chapter_recommender-systems/index.html">推荐系统</a></i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -316,7 +312,7 @@ a {
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/yi.jpg"/>
|
<img src="./_images/yi.jpg"/>
|
||||||
<h3><a href="https://vanzytay.github.io/">郑毅</a></h3>
|
<h3><a href="https://vanzytay.github.io/">郑毅</a></h3>
|
||||||
<p>谷歌
|
<p>谷歌科学家
|
||||||
<br><i><a href="http://d2l.ai/chapter_recommender-systems/index.html">推荐系统</a></i></p>
|
<br><i><a href="http://d2l.ai/chapter_recommender-systems/index.html">推荐系统</a></i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -328,42 +324,42 @@ a {
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/anirudh.jpg"/>
|
<img src="./_images/anirudh.jpg"/>
|
||||||
<h3><a href="https://github.com/AnirudhDagar">阿尼如 达格</a></h3>
|
<h3><a href="https://github.com/AnirudhDagar">阿尼如 达格</a></h3>
|
||||||
<p>亚马逊<br><i>PyTorch改编</i></p>
|
<p>印度理工学院罗克分校<br><i>PyTorch改编</i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/yuan.jpg"/>
|
<img src="./_images/yuan.jpg"/>
|
||||||
<h3><a href="https://terrytangyuan.github.io/about/">唐源</a></h3>
|
<h3><a href="https://terrytangyuan.github.io/about/">唐源</a></h3>
|
||||||
<p>Akuity<br><i>TensorFlow改编</i></p>
|
<p>蚂蚁集团高级工程师<br><i>TensorFlow改编</i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/wugaosheng.jpg"/>
|
<img src="./_images/wugaosheng.jpg"/>
|
||||||
<h3><a href="https://github.com/w5688414">吴高升</a></h3>
|
<h3><a href="https://github.com/w5688414">吴高升</a></h3>
|
||||||
<p>百度<br><i>飞桨改编</i></p>
|
<p>百度飞桨高级工程师<br><i>飞桨改编</i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/huliujun.jpg"/>
|
<img src="./_images/huliujun.jpg"/>
|
||||||
<h3><a href="https://github.com/tensorfly-gpu">胡刘俊</a></h3>
|
<h3><a href="https://github.com/tensorfly-gpu">胡刘俊</a></h3>
|
||||||
<p>百度<br><i>飞桨改编</i></p>
|
<p>飞桨开发者技术专家<br><i>飞桨改编</i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/zhangge.jpg"/>
|
<img src="./_images/zhangge.jpg"/>
|
||||||
<h3><a href="https://github.com/Shelly111111">张戈</a></h3>
|
<h3><a href="https://github.com/Shelly111111">张戈</a></h3>
|
||||||
<p>百度<br><i>飞桨改编</i></p>
|
<p>飞桨开发者技术专家<br><i>飞桨改编</i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/xiejiehang.jpg"/>
|
<img src="./_images/xiejiehang.jpg"/>
|
||||||
<h3><a href="https://github.com/JiehangXie">谢杰航</a></h3>
|
<h3><a href="https://github.com/JiehangXie">谢杰航</a></h3>
|
||||||
<p>百度<br><i>飞桨改编</i></p>
|
<p>飞桨开发者技术专家<br><i>飞桨改编</i></p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -374,14 +370,14 @@ a {
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/xiaoting.jpg"/>
|
<img src="./_images/xiaoting.jpg"/>
|
||||||
<h3><a href="https://github.com/xiaotinghe">何孝霆</a></h3>
|
<h3><a href="https://github.com/xiaotinghe">何孝霆</a></h3>
|
||||||
<p>亚马逊</p>
|
<p>亚马逊解决方案架构师</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
<div class = "mdl-cell mdl-cell--3-col mdl-cell--top">
|
||||||
<div class="author-item">
|
<div class="author-item">
|
||||||
<img src="./_images/rachel.jpeg"/>
|
<img src="./_images/rachel.jpeg"/>
|
||||||
<h3><a href="https://www.linkedin.com/in/rachelsonghu/">瑞潮儿·胡</a></h3>
|
<h3><a href="https://www.linkedin.com/in/rachelsonghu/">瑞潮儿·胡</a></h3>
|
||||||
<p>亚马逊</p>
|
<p>亚马逊科学家</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
@ -922,7 +918,7 @@ a {
|
||||||
<!--end of the map-->
|
<!--end of the map-->
|
||||||
|
|
||||||
<button class="collapsible">
|
<button class="collapsible">
|
||||||
<center>[+] <i>点击以显示不完整名单</i></center>
|
<center>[+] <i>点击以显示完整名单</i></center>
|
||||||
</button>
|
</button>
|
||||||
<div class="expansion">
|
<div class="expansion">
|
||||||
<div class="institute mdl-grid">
|
<div class="institute mdl-grid">
|
||||||
|
@ -1318,6 +1314,9 @@ a {
|
||||||
</div> <!-- mdl-grid -->
|
</div> <!-- mdl-grid -->
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<h4>如果你使用(或将要使用)本书教学并希望收到免费纸质版图书,
|
||||||
|
请<a href="https://raw.githubusercontent.com/d2l-ai/d2l-en/master/static/frontpage/attachments/hardcopy.txt" target="_blank">联系我们</a>。</h4>
|
||||||
|
|
||||||
<!-- reverse the changes by google map... -->
|
<!-- reverse the changes by google map... -->
|
||||||
<style>
|
<style>
|
||||||
a { font-weight: 400; }
|
a { font-weight: 400; }
|
||||||
|
@ -1333,12 +1332,11 @@ a {
|
||||||
<div class="highlight-python notranslate">
|
<div class="highlight-python notranslate">
|
||||||
<div class="highlight">
|
<div class="highlight">
|
||||||
<tt>
|
<tt>
|
||||||
<pre>@book{zhang2023dive,
|
<pre>@article{zhang2021dive,
|
||||||
title={Dive into Deep Learning},
|
title={Dive into Deep Learning},
|
||||||
author={Zhang, Aston and Lipton, Zachary C. and Li, Mu and Smola, Alexander J.},
|
author={Zhang, Aston and Lipton, Zachary C. and Li, Mu and Smola, Alexander J.},
|
||||||
publisher={Cambridge University Press},
|
journal={arXiv preprint arXiv:2106.11342},
|
||||||
note={\url{https://D2L.ai}},
|
year={2021}
|
||||||
year={2023}
|
|
||||||
}</pre>
|
}</pre>
|
||||||
</tt>
|
</tt>
|
||||||
</div>
|
</div>
|
||||||
|
|