ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืœืขืจื ืขืŸ ืžื™ื˜ Apache MXNet ืื•ืŸ Horovod

ื“ื™ ืื™ื‘ืขืจื–ืขืฆื•ื ื’ ืคื•ืŸ ื“ืขื ืึทืจื˜ื™ืงืœ ืื™ื– ื’ืขื•ื•ืขืŸ ืฆื•ื’ืขื’ืจื™ื™ื˜ ืื•ื™ืฃ ื“ื™ ืขืจืขื•ื• ืคื•ืŸ ื“ื™ ืึธื ื”ื™ื™ื‘ ืคื•ืŸ ื“ื™ ืงื•ืจืก ื™ื ื“ืึทืกื˜ืจื™ืึทืœ ML ืื•ื™ืฃ ื’ืจื•ื™ืก ื“ืึทื˜ืŸ

ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ื˜ืจื™ื™ื ื™ื ื’ ืื•ื™ืฃ ืงื™ื™ืคืœ ื”ื•ื™ืš-ืคืึธืจืฉื˜ืขืœื•ื ื’ ืงืึทืžืคึผื™ื•ื˜ื™ื ื’ ื™ื ืกื˜ืึทื ืกื™ื– ืงืขื ืขืŸ ืจืขื“ื•ืฆื™ืจืŸ ื“ื™ ื˜ืจื™ื™ื ื™ื ื’ ืฆื™ื™ื˜ ืคื•ืŸ ืžืึธื“ืขืจืŸ ื˜ื™ืฃ ื ืขื•ืจืึทืœ ื ืขื˜ื•ื•ืึธืจืงืก ืื•ื™ืฃ ื’ืจื•ื™ืก ืึทืžืึทื•ื ืฅ ืคื•ืŸ ื“ืึทื˜ืŸ ืคื•ืŸ ื•ื•ืึธื›ืŸ ืฆื• ืฉืขื” ืึธื“ืขืจ ืืคื™ืœื• ืžื™ื ื•ื˜, ื•ื•ืึธืก ืžืื›ื˜ ื“ืขื ื˜ืจื™ื™ื ื™ื ื’ ื˜ืขื›ื ื™ืง ืคืึทืจืฉืคึผืจื™ื™ื˜ ืื™ืŸ ืคึผืจืึทืงื˜ื™ืฉ ืึทืคึผืœืึทืงื™ื™ืฉืึทื ื– ืคื•ืŸ ื˜ื™ืฃ ืœืขืจื ืขืŸ. ื™ื•ื–ืขืจื– ืžื•ื–ืŸ ืคึฟืึทืจืฉื˜ื™ื™ืŸ ื•ื•ื™ ืฆื• ื˜ื™ื™ืœืŸ ืื•ืŸ ืกื™ื ื’ืงืจืึทื ื™ื™ื– ื“ืึทื˜ืŸ ืื™ืŸ ืงื™ื™ืคืœ ื™ื ืกื˜ืึทื ืกื™ื–, ื•ื•ืึธืก ืื™ืŸ ืงืขืจ ื”ืื˜ ืึท ื”ื•ื™ืคึผื˜ ืคึผืจืึทืœ ืื•ื™ืฃ ืกืงื™ื™ืœื™ื ื’ ืขืคืขืงื˜ื™ื•ื•ืงื™ื™ึทื˜. ืื™ืŸ ืึทื“ื™ืฉืึทืŸ, ื™ื•ื–ืขืจื– ื–ืึธืœ ืื•ื™ืš ื•ื•ื™ืกืŸ ื•ื•ื™ ืฆื• ืฆืขื•ื•ื™ืงืœืขืŸ ืึท ื˜ืจื™ื™ื ื™ื ื’ ืฉืจื™ืคื˜ ื•ื•ืึธืก ืœื•ื™ืคื˜ ืื•ื™ืฃ ืึท ืื™ื™ืŸ ื‘ื™ื™ึทืฉืคึผื™ืœ ืฆื• ืงื™ื™ืคืœ ื™ื ืกื˜ืึทื ืกื™ื–.

ืื™ืŸ ื“ืขื ืึทืจื˜ื™ืงืœ ืžื™ืจ ื•ื•ืขืœืŸ ืจืขื“ืŸ ื•ื•ืขื’ืŸ ืึท ืฉื ืขืœ ืื•ืŸ ื’ืจื™ื ื’ ื•ื•ืขื’ ืฆื• ืคืึทืจืฉืคึผืจื™ื™ื˜ืŸ ืœืขืจื ืขืŸ ืžื™ื˜ ื“ื™ ืึธืคึฟืŸ ื˜ื™ืฃ ืœืขืจื ืขืŸ ื‘ื™ื‘ืœื™ืึธื˜ืขืง Apache MXNet ืื•ืŸ ื“ื™ Horovod ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืœืขืจื ืขืŸ ืคืจื™ื™ืžื•ื•ืขืจืง. ืžื™ืจ ื•ื•ืขืœืŸ ืงืœืืจ ื‘ืึทื•ื•ื™ื™ึทื–ืŸ ื“ื™ ืคืึธืจืฉื˜ืขืœื•ื ื’ ื‘ืขื ืขืคื™ืฅ ืคื•ืŸ ื“ื™ Horovod ืคืจื™ื™ืžื•ื•ืขืจืง ืื•ืŸ ื‘ืึทื•ื•ื™ื™ึทื–ืŸ ื•ื•ื™ ืฆื• ืฉืจื™ื™ึทื‘ืŸ ืึท MXNet ื˜ืจื™ื™ื ื™ื ื’ ืฉืจื™ืคื˜ ืึทื–ื•ื™ ืึทื– ืขืก ืึทืจื‘ืขื˜ ืื™ืŸ ืึท ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืฉื˜ื™ื™ื’ืขืจ ืžื™ื˜ Horovod.

ื•ื•ืึธืก ืื™ื– Apache MXNet

ืึทืคึผืึทื˜ืฉื™ MXNet ืื™ื– ืึทืŸ ืึธืคึฟืŸ-ืžืงื•ืจ ื˜ื™ืฃ ืœืขืจื ืขืŸ ืคืจื™ื™ืžื•ื•ืขืจืง ื•ื•ืึธืก ืื™ื– ื’ืขื ื™ืฆื˜ ืฆื• ืฉืึทืคึฟืŸ, ื‘ืึทืŸ ืื•ืŸ ืฆืขื•ื•ื™ืงืœืขืŸ ื˜ื™ืฃ ื ืขื•ืจืึทืœ ื ืขื˜ื•ื•ืึธืจืงืก. MXNet ืึทื‘ืกื˜ืจืึทืงืฅ ื“ื™ ืงืึทืžืคึผืœืขืงืกื™ื˜ื™ื– ืคึฟืึทืจื‘ื•ื ื“ืŸ ืžื™ื˜ ื™ืžืคึผืœืึทืžืขื ื™ื ื’ ื ืขื•ืจืึทืœ ื ืขื˜ื•ื•ืึธืจืงืก, ืื™ื– ื”ืขื›ืกื˜ ืคึผืขืจืคืึธืจืžืึทื ืกืข ืื•ืŸ ืกืงืึทืœืึทื‘ืœืข, ืื•ืŸ ืึธืคืคืขืจืก ืึทืคึผื™ืก ืคึฟืึทืจ ืคืึธืœืงืก ืคึผืจืึธื’ืจืึทืžืžื™ื ื’ ืฉืคึผืจืึทื›ืŸ, ืึทื–ืึท ื•ื•ื™ ืคึผื™ื˜ื”ืึธืŸ, C ++, Clojure, ื–'ืื‘ื, ื“ื–ืฉื•ืœื™ืึท, R, ืกืงืึทืœืึท ืื•ืŸ ืื ื“ืขืจืข.

ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ื˜ืจื™ื™ื ื™ื ื’ ืื™ืŸ MXNet ืžื™ื˜ ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ืขืจ

ื ืึธืจืžืึทืœ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืœืขืจื ืขืŸ ืžืึธื“ื•ืœืข ืื™ืŸ MXNet ื ื™ืฆื˜ ืึท ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ืขืจ ืฆื•ื’ืึทื ื’. ืขืก ื ื™ืฆื˜ ืึท ื’ืึทื ื’ ืคื•ืŸ ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ืขืจืก ืฆื• ื–ืึทืžืœืขืŸ ื’ืจืึทื“ื™ืขื ืฅ ืคื•ืŸ ื™ืขื“ืขืจ ืึทืจื‘ืขื˜ืขืจ, ื“ื•ืจื›ืคื™ืจืŸ ืึทื’ื’ืจืขื’ืึทื˜ื™ืึธืŸ ืื•ืŸ ืฉื™ืงืŸ ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงื˜ ื’ืจืึทื“ื™ืขื ืฅ ืฆื•ืจื™ืง ืฆื• ื˜ื•ืขืจืก ืคึฟืึทืจ ื“ืขืจ ื•ื•ื™ื™ึทื˜ืขืจ ื™ื˜ืขืจืึทื˜ื™ืึธืŸ ืคื•ืŸ ืึทืคึผื˜ืึทืžืึทื–ื™ื™ืฉืึทืŸ. ื“ื™ื˜ืขืจืžืึทื ื™ื ื’ ื“ื™ ืจื™ื›ื˜ื™ืง ืคืึทืจื”ืขืœื˜ืขื ื™ืฉ ืคื•ืŸ ืกืขืจื•ื•ืขืจืก ืฆื• ื˜ื•ืขืจืก ืื™ื– ื“ืขืจ ืฉืœื™ืกืœ ืฆื• ืขืคืขืงื˜ื™ื•ื• ืกืงื™ื™ืœื™ื ื’. ืื•ื™ื‘ ืขืก ืื™ื– ื‘ืœื•ื™ื– ืื™ื™ืŸ ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ืขืจ, ืขืก ืงืขืŸ ื–ื™ื™ืŸ ืึท ื‘ืึทื˜ืึทืœื ืขืง ืื™ืŸ ื“ื™ ื—ืฉื‘ื•ื ื•ืช. ืงืึธื ื•ื•ืขืจืกืขืœื™, ืื•ื™ื‘ ืฆื• ืคื™ืœืข ืกืขืจื•ื•ืขืจืก ื–ืขื ืขืŸ ื’ืขื ื™ืฆื˜, ืคื™ืœืข-ืฆื•-ืคื™ืœืข ืงืึธืžื•ื ื™ืงืึทืฆื™ืข ืงืขื ืขืŸ ืคืึทืจืœื™ื™ื’ืŸ ืึทืœืข ื ืขืฅ ืงืึทื ืขืงืฉืึทื ื–.

ื•ื•ืึธืก ืื™ื– ื”ืึธืจืึธื•ื•ืึธื“

ื”ืึธืจืึธื•ื•ืึธื“ ืื™ื– ืึทืŸ ืึธืคึฟืŸ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ื˜ื™ืฃ ืœืขืจื ืขืŸ ืคืจื™ื™ืžื•ื•ืขืจืง ื“ืขื•ื•ืขืœืึธืคึผืขื“ ื‘ื™ื™ ื•ื‘ืขืจ. ืขืก ืœืขื•ื•ืขืจืึทื“ื–ืฉืึทื– ืขืคืขืงื˜ื™ื•ื• ืงืจื™ื™ึทื–-ื’ืคึผื• ืื•ืŸ ืงืจื™ื™ึทื–-ื ืึธื“ืข ื˜ืขืงื ืึทืœืึทื“ื–ืฉื™ื– ืึทื–ืึท ื•ื•ื™ NVIDIA Collective Communications Library (NCCL) ืื•ืŸ Message Passing Interface (MPI) ืฆื• ืคืึทืจืฉืคึผืจื™ื™ื˜ืŸ ืื•ืŸ ืฆื•ื ื•ื™ืคื’ื™ืกืŸ ืžืึธื“ืขืœ ืคึผืึทืจืึทืžืขื˜ืขืจืก ืึทืจื™ื‘ืขืจ ื•ื•ืึธืจืขืงืก. ืขืก ืึธืคึผื˜ื™ืžื™ื–ืขืก ื“ื™ ื ื•ืฆืŸ ืคื•ืŸ ื ืขืฅ ื‘ืึทื ื“ื•ื•ื™ื“ื˜ ืื•ืŸ ื•ื•ืึธื’ ื’ืขื–ื•ื ื˜ ื•ื•ืขืŸ ืืจื‘ืขื˜ืŸ ืžื™ื˜ ื˜ื™ืฃ ื ืขื•ืจืึทืœ ื ืขืฅ ืžืึธื“ืขืœืก. ืขืก ื“ืขืจื•ื•ื™ื™ึทืœ ืฉื˜ื™ืฆื˜ ืขื˜ืœืขื›ืข ืคืึธืœืงืก ืžืึทืฉื™ืŸ ืœืขืจื ืขืŸ ืคืจืึทืžืขื•ื•ืึธืจืงืก, ื ื™ื™ืžืœื™ ืžืงืก ื ืขื˜, Tensorflow, Keras ืื•ืŸ PyTorch.

MXNet ืื•ืŸ Horovod ื™ื ืึทื’ืจื™ื™ืฉืึทืŸ

MXNet ื™ื ื˜ืึทื’ืจื™ื™ืฅ ืžื™ื˜ Horovod ื“ื•ืจืš ื“ื™ ื“ื™ืกื˜ืจื™ื‘ื™ื•ื˜ื™ื“ ืœืขืจื ืขืŸ ืึทืคึผื™ืก ื“ื™ืคื™ื™ื ื“ ืื™ืŸ Horovod. ื”ืึธืจืึธื•ื•ืึธื“ ืงืึธืžื•ื ื™ืงืึทืฆื™ืข ืึทืคึผื™ืก horovod.broadcast(), horovod.allgather() ะธ horovod.allreduce() ื™ืžืคึผืœืึทืžืขื ืึทื“ ืžื™ื˜ ื™ื™ืกื™ื ื’ืงืจืึทื ืึทืก ืงืึทืœืœื‘ืึทืงืงืก ืคื•ืŸ ื“ื™ MXNet ืžืึธื˜ืึธืจ, ื•ื•ื™ ืึท ื˜ื™ื™ืœ ืคื•ืŸ ื–ื™ื™ืŸ ืึทืจื‘ืขื˜ ื’ืจืึทืคื™ืง. ืื™ืŸ ื“ืขื ื•ื•ืขื’, ื“ืึทื˜ืŸ ื“ื™ืคึผืขื ื“ืึทื ืกื™ื– ืฆื•ื•ื™ืฉืŸ ืงืึธืžื•ื ื™ืงืึทืฆื™ืข ืื•ืŸ ืงืึทืžืคึผื™ืึทื˜ื™ื™ืฉืึทืŸ ื–ืขื ืขืŸ ืœื™ื™ื›ื˜ ื›ืึทื ื“ืึทืœื“ ื“ื•ืจืš ื“ื™ MXNet ืžืึธื˜ืึธืจ ืฆื• ื•ื™ืกืžื™ื™ื“ืŸ ืคืึธืจืฉื˜ืขืœื•ื ื’ ืœืึธืกืกืขืก ืจืขื›ื˜ ืฆื• ืกื™ื ื’ืงืจืึทื ืึทื–ื™ื™ืฉืึทืŸ. ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืึธืคึผื˜ื™ืžื™ื–ืขืจ ื›ื™ื™ืคืขืฅ ื“ื™ืคื™ื™ื ื“ ืื™ืŸ ื”ืึธืจืึธื•ื•ืึธื“ ื”ืึธืจืึธื•ื•ืึธื“.ื“ื™ืกื˜ืจื™ื‘ื™ื•ื˜ืึทื“ ืึธืคึผื˜ื™ืžื™ื–ืขืจ ื™ืงืกืคึผืึทื ื“ื– ืึธืคึผื˜ื™ืžื™ื–ืขืจ ืื™ืŸ MXNet ืึทื–ื•ื™ ืึทื– ืขืก ืจื•ืคื˜ ื“ื™ ืงืึธืจืึทืกืคึผืึทื ื“ื™ื ื’ ื”ืึธืจืึธื•ื•ืึธื“ ืึทืคึผื™ืก ืคึฟืึทืจ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืคึผืึทืจืึทืžืขื˜ืขืจ ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงื•ื ื’ืขืŸ. ืึทืœืข ื“ื™ ื™ืžืคึผืœืึทืžืขื ื˜ื™ื™ืฉืึทืŸ ื“ืขื˜ืึทื™ืœืก ื–ืขื ืขืŸ ื˜ืจืึทื ืกืคึผืขืจืึทื ื˜ ืฆื• ื“ื™ ืกื•ืฃ ื ื™ืฆืขืจืก.

ืฉื ืขืœ ืึธื ื”ื™ื™ื‘

ืื™ืจ ืงืขื ืขืŸ ื’ืขืฉื•ื•ื™ื ื“ ืึธื ื”ื™ื™ื‘ืŸ ื˜ืจื™ื™ื ื™ื ื’ ืึท ืงืœื™ื™ืŸ ืงืึทื ื•ื•ืึทืœื•ืฉืึทื ืึทืœ ื ืขื•ืจืึทืœ ื ืขืฅ ืื•ื™ืฃ ื“ื™ MNIST ื“ืึทื˜ืึทื‘ื™ื™ืก ืžื™ื˜ MXNet ืื•ืŸ Horovod ืื•ื™ืฃ ื“ื™ื™ืŸ ืžืึทืงื‘ืึธืึธืง.
ืขืจืฉื˜ืขืจ, ื™ื ืกื˜ืึทืœื™ืจืŸ mxnet ืื•ืŸ horovod ืคึฟื•ืŸ PyPI:

pip install mxnet
pip install horovod

ื‘ืึทืžืขืจืงื•ื ื’: ืื•ื™ื‘ ืื™ืจ ื˜ืจืขืคืŸ ืึท ื˜ืขื•ืช ื‘ืขืฉืึทืก ืคึผื™ืคึผ ื™ื ืกื˜ืึทืœื™ืจืŸ ื”ืึธืจืึธื•ื•ืึธื“ืืคึฟืฉืจ ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืœื™ื™ื’ืŸ ืึท ื‘ื™ื™ึทื˜ืขื•ื•ื“ื™ืง MACOSX_DEPLOYMENT_TARGET=10.ื•ื•ื•ื•ื• vv - ื“ืึธืก ืื™ื– ื“ื™ ื•ื•ืขืจืกื™ืข ืคื•ืŸ โ€‹โ€‹โ€‹โ€‹ื“ื™ื™ืŸ MacOS ื•ื•ืขืจืกื™ืข, ืคึฟืึทืจ ื‘ื™ื™ึทืฉืคึผื™ืœ, ืคึฟืึทืจ MacOSX Sierra ืื™ืจ ื•ื•ืขื˜ ื“ืึทืจืคึฟืŸ ืฆื• ืฉืจื™ื™ึทื‘ืŸ MACOSX_DEPLOYMENT_TARGET=10.12 ืคึผื™ืคึผ ื™ื ืกื˜ืึทืœื™ืจืŸ ื”ืึธืจืึธื•ื•ืึธื“

ื“ืขืจื ืึธืš ื™ื ืกื˜ืึทืœื™ืจืŸ OpenMPI ืคื•ื  ื“ืึทื ืขื˜.

ืื™ืŸ ื“ื™ ืกื•ืฃ, ืืจืืคืงืืคื™ืข ื“ื™ ืคึผืจื•ื‘ื™ืจืŸ ืฉืจื™ืคื˜ mxnet_mnist.py ืคื•ื  ื“ืึทื ืขื˜ ืื•ืŸ ืœื•ื™ืคืŸ ื“ื™ ืคืืœื’ืขื ื“ืข ืงืึทืžืึทื ื“ื– ืื™ืŸ ื“ื™ ืžืึทืงื‘ืึธืึธืง ื•ื•ืึธืงื–ืึทืœ ืื™ืŸ ื“ื™ ืึทืจื‘ืขื˜ ื•ื•ืขื’ื•ื•ื™ื™ึทื–ืขืจ:

mpirun -np 2 -H localhost:2 -bind-to none -map-by slot python mxnet_mnist.py

ื“ืึธืก ื•ื•ืขื˜ ืœื•ื™ืคืŸ ื˜ืจื™ื™ื ื™ื ื’ ืื•ื™ืฃ ืฆื•ื•ื™ื™ ืงืึธืจืขืก ืคื•ืŸ ื“ื™ื™ืŸ ืคึผืจืึทืกืขืกืขืจ. ื“ืขืจ ืจืขื–ื•ืœื˜ืึทื˜ ื•ื•ืขื˜ ื–ื™ื™ืŸ ื“ื™ ืคืืœื’ืขื ื“ืข:

INFO:root:Epoch[0] Batch [0-50] Speed: 2248.71 samples/sec      accuracy=0.583640
INFO:root:Epoch[0] Batch [50-100] Speed: 2273.89 samples/sec      accuracy=0.882812
INFO:root:Epoch[0] Batch [50-100] Speed: 2273.39 samples/sec      accuracy=0.870000

ืคืึธืจืฉื˜ืขืœื•ื ื’ ื“ืขืžืึธ

ื•ื•ืขืŸ ื˜ืจื™ื™ื ื™ื ื’ ืึท ResNet50-v1 ืžืึธื“ืขืœ ืื•ื™ืฃ ืึท ImageNet ื“ืึทื˜ืึทืกืขื˜ ืื•ื™ืฃ 64 ื’ืคึผื•ืก ืžื™ื˜ ืึทื›ื˜ ื™ื ืกื˜ืึทื ืกื™ื– p3.16xlarge EC2, ื™ืขื“ืขืจ ืžื™ื˜ 8 NVIDIA Tesla V100 GPUs ืื•ื™ืฃ AWS ื•ื•ืึธืœืงืŸ, ืžื™ืจ ืึทื˜ืฉื™ื•ื•ื“ ืึท ื˜ืจื™ื™ื ื™ื ื’ ื˜ืจื•ืคึผื•ื˜ ืคื•ืŸ 45000 ื‘ื™ืœื“ืขืจ / ืกืขืง (ื“"ื” ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ื˜ืจื™ื™ื ื“ ืกืึทืžืคึผืึทืœื– ืคึผืขืจ ืกืขืงื•ื ื“ืข). ื˜ืจืึทื™ื ื™ื ื’ ื’ืขืขื ื“ื™ืงื˜ ืื™ืŸ 44 ืžื™ื ื•ื˜ ื ืึธืš 90 ืขืคึผืึธืก ืžื™ื˜ ืึท ื‘ืขืกื˜ืขืจ ืึทืงื™ืขืจืึทืกื™ ืคื•ืŸ 75.7%.

ืžื™ืจ ืงืึทืžืคึผืขืจื“ ื“ืขื ืฆื• MXNet ืก ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ื˜ืจื™ื™ื ื™ื ื’ ืฆื•ื’ืึทื ื’ ืคื•ืŸ ื ื™ืฆืŸ ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ืขืจืก ืื•ื™ืฃ 8, 16, 32 ืื•ืŸ 64 ื’ืคึผื•ืก ืžื™ื˜ ืึท ืื™ื™ืŸ ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ืขืจ ืื•ืŸ ืึท ืกืขืจื•ื•ืขืจ ืฆื• ืึทืจื‘ืขื˜ืขืจ ืคืึทืจื”ืขืœื˜ืขื ื™ืฉ ืคื•ืŸ 1 ืฆื• 1 ืื•ืŸ 2 ืฆื• 1 ืจื™ืกืคึผืขืงื˜ื™ื•ื•ืœื™. ืื™ืจ ืงืขื ืขืŸ ื–ืขืŸ ื“ื™ ืจืขื–ื•ืœื˜ืึทื˜ ืื™ืŸ ืคื™ื’ื•ืจืข 1 ืื•ื ื˜ืŸ. ืื•ื™ืฃ ื“ื™ ื™-ืึทืงืก ืื•ื™ืฃ ื“ื™ ืœื™ื ืงืก, ื“ื™ ื‘ืึทืจืก ืจืขืคึผืจืึทื–ืขื ืฅ ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ื‘ื™ืœื“ืขืจ ืฆื• ื‘ืึทืŸ ืคึผืขืจ ืกืขืงื•ื ื“ืข, ื“ื™ ืฉื•ืจื•ืช ืคืึทืจื˜ืจืึทื›ื˜ื  ื–ื™ืš ื“ื™ ืกืงื™ื™ืœื™ื ื’ ืขืคืขืงื˜ื™ื•ื•ืงื™ื™ึทื˜ (ื“ืึธืก ืื™ื–, ื“ื™ ืคืึทืจื”ืขืœื˜ืขื ื™ืฉ ืคื•ืŸ ืคืึทืงื˜ื™ืฉ ืฆื• ื™ื“ืขืึทืœ ื˜ืจื•ืคึผื•ื˜) ืื•ื™ืฃ ื“ื™ ื™-ืึทืงืก ืื•ื™ืฃ ื“ื™ ืจืขื›ื˜. ื•ื•ื™ ืื™ืจ ืงืขื ืขืŸ ื–ืขืŸ, ื“ื™ ื‘ืจื™ืจื” ืคื•ืŸ ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ืกืขืจื•ื•ืขืจืก ืึทืคืขืงืฅ ื“ื™ ืกืงื™ื™ืœื™ื ื’ ืขืคืขืงื˜ื™ื•ื•ืงื™ื™ึทื˜. ืื•ื™ื‘ ืขืก ืื™ื– ื‘ืœื•ื™ื– ืื™ื™ืŸ ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ืขืจ, ื“ื™ ืกืงื™ื™ืœื™ื ื’ ืขืคืขืงื˜ื™ื•ื•ืงื™ื™ึทื˜ ืคืึทืœืŸ ืฆื• 38% ืื•ื™ืฃ 64 ื’ืคึผื•ืก. ืฆื• ื“ืขืจื’ืจื™ื™ื›ืŸ ื“ื™ ื–ืขืœื‘ืข ืกืงื™ื™ืœื™ื ื’ ืขืคืขืงื˜ื™ื•ื•ืงื™ื™ึทื˜ ื•ื•ื™ ืžื™ื˜ ื”ืึธืจืึธื•ื•ืึธื“, ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ื˜ืึธืคึผืœ ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ืกืขืจื•ื•ืขืจืก ืงืึธืจืขื•ื• ืฆื• ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ื˜ื•ืขืจืก.

ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืœืขืจื ืขืŸ ืžื™ื˜ Apache MXNet ืื•ืŸ Horovod
ืคื™ื’ื•ืจืข 1. ืคืึทืจื’ืœื™ื™ึทืš ืคื•ืŸ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืœืขืจื ืขืŸ ื ื™ืฆืŸ ืžืงืกื ืขื˜ ืžื™ื˜ ื”ืึธืจืึธื•ื•ืึธื“ ืื•ืŸ ืžื™ื˜ ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ืขืจ

ืื™ืŸ ื˜ืึทื‘ืœืข 1 ืื•ื ื˜ืŸ, ืžื™ืจ ืคืึทืจื’ืœื™ื™ึทื›ืŸ ื“ื™ ืœืขืฆื˜ ืคึผืจื™ื™ึทื– ืคึผืขืจ ื‘ื™ื™ึทืฉืคึผื™ืœ ื•ื•ืขืŸ ืคืœื™ืกื ื“ื™ืง ื™ืงืกืคึผืขืจืึทืžืึทื ืฅ ืื•ื™ืฃ 64 ื’ืคึผื•ืก. ื ื™ืฆืŸ MXNet ืžื™ื˜ Horovod ื’ื™ื˜ ื“ื™ ื‘ืขืกื˜ืขืจ ื˜ืจื•ืคึผื•ื˜ ืฆื• ื“ื™ ืœืึธื•ืึทืกื˜ ืคึผืจื™ื™ึทื–.

ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืœืขืจื ืขืŸ ืžื™ื˜ Apache MXNet ืื•ืŸ Horovod
ื˜ื™ืฉ 1. ืคึผืจื™ื™ึทื– ืคืึทืจื’ืœื™ื™ึทืš ืฆื•ื•ื™ืฉืŸ ื”ืึธืจืึธื•ื•ืึธื“ ืื•ืŸ ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ื™ืจืขืจ ืžื™ื˜ ืึท ืกืขืจื•ื•ืขืจ ืฆื• ืึทืจื‘ืขื˜ืขืจ ืคืึทืจื”ืขืœื˜ืขื ื™ืฉ ืคื•ืŸ 2 ืฆื• 1.

ืกื˜ืขืคึผืก ืฆื• ืจืขืคึผืจืึธื“ื•ืฆื™ืจืŸ

ืื™ืŸ ื“ืขืจ ื•ื•ื™ื™ึทื˜ืขืจ ืกื˜ืขืคึผืก, ืžื™ืจ ื•ื•ืขืœืŸ ื•ื•ื™ื™ึทื–ืŸ ืื™ืจ ื•ื•ื™ ืฆื• ืจืขืคึผืจืึธื“ื•ืฆื™ืจืŸ ื“ื™ ืจืขื–ื•ืœื˜ืึทื˜ ืคื•ืŸ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ื˜ืจื™ื™ื ื™ื ื’ ืžื™ื˜ MXNet ืื•ืŸ Horovod. ืœื™ื™ืขื ืขืŸ ืžืขืจ ื•ื•ืขื’ืŸ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืœืขืจื ืขืŸ ืžื™ื˜ MXNet ื“ืขื ืคึผืึธืกื˜ืŸ.

ืฉืจื™ื˜ ืงืกื ื•ืžืงืก

ืฉืึทืคึฟืŸ ืึท ืงื ื•ื™ืœ ืคื•ืŸ ื›ืึธื•ืžืึทื“ื–ืฉื™ื ื™ืึทืก ื™ื ืกื˜ืึทื ืกื™ื– ืžื™ื˜ MXNet ื•ื•ืขืจืกื™ืข 1.4.0 ืึธื“ืขืจ ื”ืขื›ืขืจ ืื•ืŸ Horovod ื•ื•ืขืจืกื™ืข 0.16.0 ืึธื“ืขืจ ื”ืขื›ืขืจ ืฆื• ื ื•ืฆืŸ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืœืขืจื ืขืŸ. ืื™ืจ ื•ื•ืขื˜ ืื•ื™ืš ื“ืึทืจืคึฟืŸ ืฆื• ื™ื ืกื˜ืึทืœื™ืจืŸ ืœื™ื™ื‘ืจืขืจื™ื– ืคึฟืึทืจ ื’ืคึผื• ื˜ืจื™ื™ื ื™ื ื’. ืคึฟืึทืจ ืื•ื ื“ื–ืขืจ ื™ื ืกื˜ืึทื ืกื™ื–, ืžื™ืจ ืื•ื™ืกื“ืขืจื•ื•ื™ื™ืœื˜ Ubuntu 16.04 ืœื™ื ื•ืงืก, ืžื™ื˜ ื’ืคึผื• ื“ืจื™ื™ื•ื•ืขืจ 396.44, CUDA 9.2, CUDNN 7.2.1 ื‘ื™ื‘ืœื™ืึธื˜ืขืง, NCCL 2.2.13 ืงืึทืžื™ื•ื ืึทืงื™ื™ื˜ืขืจ ืื•ืŸ OpenMPI 3.1.1. ืื•ื™ืš ืื™ืจ ืงืขื ืขืŸ ื ื•ืฆืŸ Amazon Deep Learning AMI, ื•ื•ื• ื“ื™ ืœื™ื™ื‘ืจืขืจื™ื– ื–ืขื ืขืŸ ืฉื•ื™ืŸ ืคืึทืจ-ืื™ื ืกื˜ืึทืœื™ืจืŸ.

ืฉืจื™ื˜ ืงืกื ื•ืžืงืก

ืœื™ื™ื’ ื“ื™ ืคื™ื™ื™ืงื™ื™ื˜ ืฆื• ืึทืจื‘ืขื˜ืŸ ืžื™ื˜ ื“ื™ Horovod API ืฆื• ื“ื™ื™ืŸ MXNet ื˜ืจื™ื™ื ื™ื ื’ ืฉืจื™ืคื˜. ื“ื™ ืื•ื ื˜ืŸ ืฉืจื™ืคื˜ ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ื“ื™ MXNet Gluon API ืงืขื ืขืŸ ื–ื™ื™ืŸ ื’ืขื•ื•ื™ื™ื ื˜ ื•ื•ื™ ืึท ืคึผืฉื•ื˜ ืžื•ืกื˜ืขืจ. ื“ื™ ืฉื•ืจื•ืช ืื™ืŸ ื“ืจื™ื™ืกื˜ ื–ืขื ืขืŸ ื“ืืจืฃ ืื•ื™ื‘ ืื™ืจ ืฉื•ื™ืŸ ื”ืึธื‘ืŸ ืึท ืงืึธืจืึทืกืคึผืึทื ื“ื™ื ื’ ื˜ืจื™ื™ื ื™ื ื’ ืฉืจื™ืคื˜. ื“ืึธ ื–ืขื ืขืŸ ืึท ื‘ื™ืกืœ ืงืจื™ื˜ื™ืฉ ืขื ื“ืขืจื•ื ื’ืขืŸ ืื™ืจ ื“ืึทืจืคึฟืŸ ืฆื• ืžืึทื›ืŸ ืฆื• ืœืขืจื ืขืŸ ืžื™ื˜ Horovod:

  • ืฉื˜ืขืœืŸ ื“ืขื ืงืึธื ื˜ืขืงืกื˜ ืœื•ื™ื˜ ื“ื™ ื”ื™ื’ืข ื”ืึธืจืึธื•ื•ืึธื“ ืจื™ื™ (ืฉื•ืจื” 8) ืฆื• ืคึฟืึทืจืฉื˜ื™ื™ืŸ ืึทื– ื˜ืจื™ื™ื ื™ื ื’ ืื™ื– ื“ื•ืจื›ื’ืขืงืึธื›ื˜ ืื•ื™ืฃ ื“ื™ ืจื™ื›ื˜ื™ืง ื’ืจืึทืคื™ืงืก ื”ืึทืจืฅ.
  • ืคืึธืจืŸ ืขืจืฉื˜ ืคึผืึทืจืึทืžืขื˜ืขืจืก ืคื•ืŸ ืื™ื™ืŸ ืึทืจื‘ืขื˜ืขืจ ืฆื• ืึทืœืข (ืฉื•ืจื” 18) ืฆื• ืขื ืฉื•ืจ ืึทื– ืึทืœืข ื˜ื•ืขืจืก ืึธื ื”ื™ื™ื‘ืŸ ืžื™ื˜ ื“ื™ ื–ืขืœื‘ืข ืขืจืฉื˜ ืคึผืึทืจืึทืžืขื˜ืขืจืก.
  • ืฉืึทืคึฟืŸ ืึท ื”ืึธืจืึธื•ื•ืึธื“ ื“ื™ืกื˜ืจื™ื‘ื™ื•ื˜ื™ื“ ืึธืคึผื˜ื™ืžื™ื–ืขืจ (ืฉื•ืจื” 25) ืฆื• ื“ืขืจื”ื™ื™ึทื ื˜ื™ืงืŸ ื“ื™ ืคึผืึทืจืึทืžืขื˜ืขืจืก ืื™ืŸ ืึท ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืฉื˜ื™ื™ื’ืขืจ.

ืฆื• ื‘ืึทืงื•ืžืขืŸ ื“ื™ ืคื•ืœ ืฉืจื™ืคื˜, ื‘ื™ื˜ืข ืึธืคึผืฉื™ืงืŸ ืฆื• ื“ื™ Horovod-MXNet ื‘ื™ื™ืฉืคื™ืœืŸ MNIST ะธ ื™ืžืึทื’ืขื ืขื˜.

1  import mxnet as mx
2  import horovod.mxnet as hvd
3
4  # Horovod: initialize Horovod
5  hvd.init()
6
7  # Horovod: pin a GPU to be used to local rank
8  context = mx.gpu(hvd.local_rank())
9
10 # Build model
11 model = ...
12
13 # Initialize parameters
14 model.initialize(initializer, ctx=context)
15 params = model.collect_params()
16
17 # Horovod: broadcast parameters
18 hvd.broadcast_parameters(params, root_rank=0)
19
20 # Create optimizer
21 optimizer_params = ...
22 opt = mx.optimizer.create('sgd', **optimizer_params)
23
24 # Horovod: wrap optimizer with DistributedOptimizer
25 opt = hvd.DistributedOptimizer(opt)
26
27 # Create trainer and loss function
28 trainer = mx.gluon.Trainer(params, opt, kvstore=None)
29 loss_fn = ...
30
31 # Train model
32 for epoch in range(num_epoch):
33    ...

ืฉืจื™ื˜ ืงืกื ื•ืžืงืก

ืงืœืึธืฅ ืื™ืŸ ืฆื• ืื™ื™ื ืขืจ ืคื•ืŸ ื“ื™ ื˜ื•ืขืจืก ืฆื• ืึธื ื”ื™ื™ื‘ืŸ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ื˜ืจื™ื™ื ื™ื ื’ ื ื™ืฆืŸ ื“ื™ MPI ื“ื™ืจืขืงื˜ื™ื•ื•. ืื™ืŸ ื“ืขื ื‘ื™ื™ึทืฉืคึผื™ืœ, ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ื˜ืจื™ื™ื ื™ื ื’ ืœื•ื™ืคื˜ ืื•ื™ืฃ ืคื™ืจ ื™ื ืกื˜ืึทื ืกื™ื– ืžื™ื˜ 4 ื’ืคึผื•ืก ื™ืขื“ืขืจ, ืื•ืŸ ืึท ื’ืึทื ืฅ ืคื•ืŸ 16 ื’ืคึผื•ืก ืื™ืŸ ื“ืขื ืงื ื•ื™ืœ. ื“ื™ ืกื˜ืึธื˜ืฉืึทืกื˜ื™ืง ื’ืจืึทื“ื™ืขื ื˜ ืึทืจืึธืคึผื’ืึทื ื’ (SGD) ืึธืคึผื˜ื™ืžื™ื–ืขืจ ื•ื•ืขื˜ ื–ื™ื™ืŸ ื’ืขื•ื•ื™ื™ื ื˜ ืžื™ื˜ ื“ื™ ืคืืœื’ืขื ื“ืข ื›ื™ื™ืคึผืขืจืคึผืึทืจืึทืžืึทื˜ืขืจ:

  • ืžื™ื ื™ ืคึผืขืงืœ ื’ืจื™ื™ืก: 256
  • ืœืขืจื ืขืŸ ืงื•ืจืก: 0.1
  • ืžืึธืžืขื ื˜ื•ื: 0.9
  • ื•ื•ืึธื’ ืคืึทืจืคื•ื™ืœืŸ: 0.0001

ื•ื•ืขืŸ ืžื™ืจ ืกืงื™ื™ืœื“ ืคื•ืŸ ืื™ื™ืŸ ื’ืคึผื• ืฆื• 64 ื’ืคึผื•ืก, ืžื™ืจ ืœื™ื ืขืึทืจืœื™ ืกืงื™ื™ืœื“ ื“ื™ ื˜ืจื™ื™ื ื™ื ื’ ืงื•ืจืก ืœื•ื™ื˜ ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ื’ืคึผื•ืก (ืคื•ืŸ 0,1 ืคึฟืึทืจ 1 ื’ืคึผื• ืฆื• 6,4 ืคึฟืึทืจ 64 ื’ืคึผื•), ื‘ืฉืขืช ืžื™ืจ ื”ืึทืœื˜ืŸ ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ื‘ื™ืœื“ืขืจ ืคึผืขืจ ื’ืคึผื• ื‘ื™ื™ 256 (ืคื•ืŸ ืึท ืคึผืขืงืœ ืคื•ืŸ 256 ื‘ื™ืœื“ืขืจ ืคึฟืึทืจ 1 ื’ืคึผื• ืฆื• 16 ืคึฟืึทืจ 384 ื’ืคึผื•). ื“ื™ ื•ื•ืึธื’ ืคืึทืจืคื•ื™ืœืŸ ืื•ืŸ ืžืึธืžืขื ื˜ื•ื ืคึผืึทืจืึทืžืขื˜ืขืจืก ื’ืขื‘ื™ื˜ืŸ ื•ื•ื™ ื“ื™ ื ื•ืžืขืจ ืคื•ืŸ ื’ืคึผื•ืก ื’ืขื•ื•ืืงืกืŸ. ืžื™ืจ ื’ืขื•ื•ื™ื™ื ื˜ ื’ืขืžื™ืฉื˜ ืคึผื™ื ื˜ืœืขื›ืงื™ื™ึทื˜ ื˜ืจื™ื™ื ื™ื ื’ ืžื™ื˜ ื“ื™ ืคืœืึธื•ื˜ 64 ื“ืึทื˜ืŸ ื˜ื™ืคึผ ืคึฟืึทืจ ื“ื™ ืคืึธืจื•ื™ืก ืคืึธืจืŸ ืื•ืŸ ืคืœืึธื•ื˜ 16 ืคึฟืึทืจ ื’ืจืึทื“ื™ืึทื ืฅ ืฆื• ืคืึทืจื’ื™ื›ืขืจืŸ ื“ื™ ืคืœืึธื•ื˜ 32 ื—ืฉื‘ื•ื ื•ืช ื’ืขืฉื˜ื™ืฆื˜ ื“ื•ืจืš NVIDIA ื˜ืขืกืœืึท ื’ืคึผื•ืก.

$ mpirun -np 16 
    -H server1:4,server2:4,server3:4,server4:4 
    -bind-to none -map-by slot 
    -mca pml ob1 -mca btl ^openib 
    python mxnet_imagenet_resnet50.py

ืกืึธืฃ

ืื™ืŸ ื“ืขื ืึทืจื˜ื™ืงืœ, ืžื™ืจ ื’ืขืงื•ืงื˜ ืื•ื™ืฃ ืึท ืกืงืึทืœืึทื‘ืœืข ืฆื•ื’ืึทื ื’ ืฆื• ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืžืึธื“ืขืœ ื˜ืจื™ื™ื ื™ื ื’ ืžื™ื˜ Apache MXNet ืื•ืŸ Horovod. ืžื™ืจ ื“ืขืžืึทื ืกื˜ืจื™ื™ื˜ื™ื“ ื“ื™ ืกืงื™ื™ืœื™ื ื’ ืขืคืขืงื˜ื™ื•ื•ืงื™ื™ึทื˜ ืื•ืŸ ืงืึธืก-ื™ืคืขืงื˜ื™ื•ื•ื ืึทืก ืงืึทืžืคึผืขืจื“ ืžื™ื˜ ื“ื™ ืคึผืึทืจืึทืžืขื˜ืขืจ ืกืขืจื•ื•ืขืจ ืฆื•ื’ืึทื ื’ ืื•ื™ืฃ ื“ื™ ImageNet ื“ืึทื˜ืึทืกืขื˜ ืื•ื™ืฃ ื•ื•ืึธืก ื“ื™ ResNet50-v1 ืžืึธื“ืขืœ ืื™ื– ื˜ืจื™ื™ื ื“. ืžื™ืจ ื”ืึธื‘ืŸ ืื•ื™ืš ืึทืจื™ื™ึทื ื’ืขืจืขื›ื ื˜ ืกื˜ืขืคึผืก ื•ื•ืึธืก ืื™ืจ ืงืขื ืขืŸ ื ื•ืฆืŸ ืฆื• ืžืึธื“ื™ืคื™ืฆื™ืจืŸ ืึท ื™ื’ื–ื™ืกื˜ื™ื ื’ ืฉืจื™ืคื˜ ืฆื• ืœื•ื™ืคืŸ ืžื•ืœื˜ื™-ื™ื ืกื˜ืึทื ืก ื˜ืจื™ื™ื ื™ื ื’ ืžื™ื˜ Horovod.

ืื•ื™ื‘ ืื™ืจ ื ืึธืจ ืึธื ื”ื™ื™ื‘ืŸ ืžื™ื˜ MXNet ืื•ืŸ ื˜ื™ืฃ ืœืขืจื ืขืŸ, ื’ื™ื™ืŸ ืฆื• ื“ื™ ื™ื™ึทื ืžืึธื ื˜ื™ืจื•ื ื’ ื‘ืœืึทื˜ MXNeืฆื• ืขืจืฉื˜ืขืจ ื‘ื•ื™ืขืŸ MXNet. ืžื™ืจ ืื•ื™ืš ืจืขืงืึธืžืขื ื“ื™ืจืŸ ืฆื• ืœื™ื™ืขื ืขืŸ ื“ืขื ืึทืจื˜ื™ืงืœ MXNet ืื™ืŸ 60 ืžื™ื ื•ื˜ืฆื• ืึธื ื”ื™ื™ื‘ืŸ.

ืื•ื™ื‘ ืื™ืจ ื”ืึธื˜ ืฉื•ื™ืŸ ื’ืขืืจื‘ืขื˜ ืžื™ื˜ MXNet ืื•ืŸ ืื™ืจ ื•ื•ื™ืœืŸ ืฆื• ืคึผืจื•ื‘ื™ืจืŸ ืคื•ื ืื ื“ืขืจื’ืขื˜ื™ื™ืœื˜ ืœืขืจื ืขืŸ ืžื™ื˜ Horovod, ืงื•ืง ืื™ืŸ ื”ื•ืจืึธื•ื•ืึธื“ ื™ื ืกื˜ืึทืœื™ืจื•ื ื’ ื‘ืœืึทื˜, ื‘ื•ื™ืขืŸ ืขืก ืคึฟื•ืŸ MXNet ืื•ืŸ ื ืึธื›ื’ื™ื™ืŸ ื“ืขื ื‘ื™ื™ึทืฉืคึผื™ืœ MNIST ืึธื“ืขืจ ื™ืžืึทื’ืขื ืขื˜.

* ืคึผืจื™ื™ึทื– ืื™ื– ืงืึทืœืงื™ืึทืœื™ื™ื˜ื™ื“ ื‘ืื–ื™ืจื˜ ืื•ื™ืฃ ืึทื•ืจืœื™ ืจื™ื™ืฅ AWS ืคึฟืึทืจ EC2 ื™ื ืกื˜ืึทื ืกื™ื–

ืœืขืจื ืขืŸ ืžืขืจ ื•ื•ืขื’ืŸ ื“ืขื ืงื•ืจืก ื™ื ื“ืึทืกื˜ืจื™ืึทืœ ML ืื•ื™ืฃ ื’ืจื•ื™ืก ื“ืึทื˜ืŸ

ืžืงื•ืจ: www.habr.com

ืœื™ื™ื’ืŸ ืึท ื‘ืึทืžืขืจืงื•ื ื’