MLOps - د پخلي کتاب، لومړی څپرکی

MLOps - د پخلي کتاب، لومړی څپرکی

سلام و ټولو ته! زه په CROC کې د CV جوړونکی یم. موږ اوس د 3 کلونو لپاره د CV په ساحه کې پروژې پلي کوو. د دې وخت په جریان کې، موږ ډیری شیان ترسره کړل، د بیلګې په توګه: موږ د موټر چلوونکو څارنه کوله ترڅو د موټر چلولو په وخت کې دوی څښاک نه کوي، سګرټ نه څکوي، په تلیفون خبرې نه کوي، سړک ته ګوري، او په خوبونو یا وريځو کې نه وي. ; موږ هغه خلک ثبت کړل چې په ځانګړي لارو کې موټر چلوي او د پارکینګ ډیری ځایونه نیسي؛ ډاډ ترلاسه کړئ چې کارګران هیلمټ، دستکشې او نور اغوندي؛ یو کارمند پیژندل شوی چې غواړي تاسیس ته ننوځي؛ موږ هرڅه چې کولی شو حساب کړو.

زه دا ټول د څه لپاره کوم؟

د پروژو د پلي کولو په بهیر کې، موږ ټکانونه ووهل، ډیری خنډونه، ځینې ستونزې چې تاسو ورسره اشنا یاست یا په راتلونکي کې به ورسره اشنا شئ.

راځئ چې وضعیت سم کړو

راځئ تصور وکړو چې موږ په یو ځوان شرکت "N" کې دنده ترلاسه کړې، چې فعالیتونه یې د ML سره تړاو لري. موږ په ML (DL, CV) پروژه کار کوو، بیا د کوم دلیل لپاره موږ بل کار ته مخه کوو، عموما یو وقفه اخلو، او خپل ځان یا د بل چا نیورون ته راستون شو.

  1. د حقیقت شیبه راځي ، تاسو اړتیا لرئ په یو ډول په یاد ولرئ چې تاسو چیرته ودریږئ ، کوم هایپرپرامیټرونه چې تاسو یې هڅه کړې او خورا مهم ، د کومې پایلې لامل شوي. د دې لپاره ډیری اختیارونه شتون لري چې څوک په ټولو لانچونو کې معلومات ذخیره کړي: په سر کې ، کنفیګس ، نوټ پیډ ، په بادل کې په کاري چاپیریال کې. ما داسې یو اختیار ولید کله چې هایپرپرامیټرونه په کوډ کې د تبصره شوي لینونو په توګه زیرمه شوي و، په عموم کې، د فینسي الوتنه. اوس تصور وکړئ چې تاسو خپلې پروژې ته نه، بلکې د هغه چا پروژې ته راستانه شوي یاست چې شرکت یې پریښی او تاسو ته د model_1.pb په نوم یو کوډ او ماډل په میراث پاتې دی. د انځور بشپړولو او ټول درد رسولو لپاره، راځئ چې تصور وکړو چې تاسو هم یو ابتدايي متخصص یاست.
  2. وړاندې ځه. د کوډ چلولو لپاره، موږ او هرڅوک چې د دې سره کار وکړي باید یو چاپیریال رامنځته کړو. دا ډیری وختونه پیښیږي چې د کوم دلیل لپاره دوی هغه زموږ د میراث په توګه نه پریږدي. دا هم یو غیر معمولی کار کیدی شي. تاسو نه غواړئ په دې ګام کې وخت ضایع کړئ، ایا تاسو؟
  3. موږ یو ماډل روزلو (د مثال په توګه، د موټر کشف کوونکی). موږ هغه ځای ته ورسیږو چیرې چې دا خورا ښه کیږي - دا د پایلې خوندي کولو وخت دی. راځئ چې دې ته car_detection_v1.pb ووایو. بیا موږ یو بل ته روزنه ورکوو - car_detection_v2.pb. یو څه وخت وروسته، زموږ همکاران یا موږ پخپله د مختلف جوړښتونو په کارولو سره ډیر څه زده کوو. د پایلې په توګه، د هنري اثارو یوه ډله جوړه شوې، چې په اړه یې معلومات باید په ډیر دقت سره راټول شي (مګر موږ به دا وروسته ترسره کړو، ځکه چې د اوس لپاره موږ ډیر لومړیتوبونه لرو).
  4. Ну вот и всё! У нас есть модель! Мы можем приступать к обучению следующей модели, к разработке архитектуры для решения новой задачи или можем пойти попить чай? А деплоить кто будет?

د ستونزو پیژندنه

په یوه پروژه یا محصول کار کول د ډیری خلکو کار دی. او د وخت په تیریدو سره، خلک پریږدي او راځي، ډیرې پروژې شتون لري، او پروژې پخپله پیچلې کیږي. په یو ډول یا بل ډول ، د پورته بیان شوي دورې حالتونه (او نه یوازې) په ځینې ترکیبونو کې به له تکرار څخه تکرار ته پیښ شي. دا ټول د ضایع وخت، ګډوډۍ، اعصابو، د احتمالي پیرودونکو نا رضایتۍ، او بالاخره د پیسو ضایع کیدو پایله لري. که څه هم موږ ټول معمولا ورته زاړه ریک تعقیب کوو، زه باور لرم چې هیڅوک نه غواړي دا شیبې بیا تکرار کړي.

MLOps - د پخلي کتاب، لومړی څپرکی

نو، موږ د یو پرمختیایي دورې څخه تیر شوي یو او موږ ګورو چې ستونزې شتون لري چې باید حل شي. د دې کولو لپاره تاسو اړتیا لرئ:

  • په اسانۍ سره د کار پایلې ذخیره کول؛
  • د نوي کارمندانو د ګډون پروسه ساده کړئ؛
  • د پرمختیایي چاپیریال د ځای پرځای کولو پروسه ساده کول؛
  • د ماډل نسخه کولو پروسه تنظیم کړئ؛
  • د موډلونو اعتبار کولو لپاره اسانه لار ولرئ؛
  • د دولت د مدیریت یوه نمونه وسیله پیدا کړئ؛
  • تولید ته د ماډلونو رسولو لپاره لاره ومومئ.

په ښکاره ډول دا اړینه ده چې د کاري فلو سره راشي چې تاسو ته اجازه درکوي په اسانۍ او اسانۍ سره د دې ژوند دوره اداره کړئ؟ دې عمل ته MLOps ویل کیږي

MLOps، یا د ماشین زده کړې لپاره DevOps، د ډیټا ساینس او ​​IT ټیمونو ته اجازه ورکوي چې د ماشین زده کړې ماډلونو لپاره د څارنې، اعتبار، او حکومتدارۍ له لارې د ماډل پراختیا او ګمارنې سرعت سره همکاري وکړي.

تاسو کولی شئ لوستلد ګوګل هلکان د دې ټولو په اړه څه فکر کوي؟ د مقالې څخه دا روښانه ده چې MLOps خورا لوی شی دی.

MLOps - د پخلي کتاب، لومړی څپرکی

نور زما په مقاله کې به زه د پروسې یوازې برخه بیان کړم. د تطبیق لپاره، زه به د MLflow وسیله وکاروم، ځکه چې ... دا د خلاصې سرچینې پروژه ده، د نښلولو لپاره لږ مقدار کوډ ته اړتیا ده او د مشهور ایم ایل چوکاټونو سره ادغام شتون لري. تاسو کولی شئ د نورو وسیلو لپاره انټرنیټ وپلټئ، لکه کیوب فلو، سیج میکر، ټرینونه، او داسې نور، او شاید یو داسې ومومئ چې ستاسو اړتیاوو سره سم وي.

د MLFlow وسیلې کارولو مثال په کارولو سره MLOps "جوړول"

MLFlow د ML ماډلونو د ژوند دورې مدیریت لپاره د خلاصې سرچینې پلیټ فارم دی (https://mlflow.org/).

MLflow څلور برخې لري:

  • د MLflow تعقیب - د ثبت کولو پایلو او پیرامیټونو مسلې پوښي چې د دې پایلې لامل شوي؛
  • د MLflow پروژه - تاسو ته اجازه درکوي کوډ بسته کړئ او په هر پلیټ فارم کې یې بیا تولید کړئ؛
  • د MLflow ماډلونه - تولید ته د ماډلونو ځای په ځای کولو مسؤلیت؛
  • د MLflow راجسټری - تاسو ته اجازه درکوي ماډلونه ذخیره کړئ او د دوی حالت په مرکزي ذخیره کې اداره کړئ.

MLflow په دوو ادارو کار کوي:

  • لانچ د روزنې، پیرامیټونو او میټریکونو بشپړ پړاو دی چې موږ یې راجستر کول غواړو؛
  • تجربه یوه "موضوع" ده چې یوځای پرمخ ځي.

د مثال ټول مرحلې په اوبنټو 18.04 عملیاتي سیسټم کې پلي کیږي.

1. سرور ځای پرځای کړئ

د دې لپاره چې موږ په اسانۍ سره خپله پروژه اداره کړو او ټول اړین معلومات ترلاسه کړو، موږ به یو سرور ځای په ځای کړو. د MLflow تعقیب سرور دوه اصلي برخې لري:

  • بیکینډ پلورنځی - د راجستر شوي ماډلونو په اړه د معلوماتو ذخیره کولو مسؤلیت لري (د 4 DBMSs ملاتړ کوي: mysql, mssql, sqlite, and postgresql);
  • د هنري توکو پلورنځی - د هنري اثارو ذخیره کولو مسؤلیت لري (د 7 ذخیره کولو اختیارونو ملاتړ کوي: ایمیزون S3 ، Azure بلاب ذخیره ، د ګوګل کلاوډ ذخیره ، FTP سرور ، SFTP سرور ، NFS ، HDFS).

د د هنري توکو پلورنځی د سادگي لپاره، راځئ چې د sftp سرور واخلو.

  • یوه ډله جوړه کړئ
    $ sudo groupadd sftpg
  • یو کارن اضافه کړئ او د هغه لپاره پټنوم ترتیب کړئ
    $ sudo useradd -g sftpg mlflowsftp
    $ sudo passwd mlflowsftp 
  • د څو لاسرسي ترتیبات تنظیم کول
    $ sudo mkdir -p /data/mlflowsftp/upload
    $ sudo chown -R root.sftpg /data/mlflowsftp
    $ sudo chown -R mlflowsftp.sftpg /data/mlflowsftp/upload
  • /etc/ssh/sshd_config ته یو څو کرښې اضافه کړئ
    Match Group sftpg
     ChrootDirectory /data/%u
     ForceCommand internal-sftp
  • خدمت بیا پیل کړئ
    $ sudo systemctl restart sshd

د شاته پلورنځی راځئ چې postgresql واخلو.

$ sudo apt update
$ sudo apt-get install -y postgresql postgresql-contrib postgresql-server-dev-all
$ sudo apt install gcc
$ pip install psycopg2
$ sudo -u postgres -i
# Create new user: mlflow_user
[postgres@user_name~]$ createuser --interactive -P
Enter name of role to add: mlflow_user
Enter password for new role: mlflow
Enter it again: mlflow
Shall the new role be a superuser? (y/n) n
Shall the new role be allowed to create databases? (y/n) n
Shall the new role be allowed to create more new roles? (y/n) n
# Create database mlflow_bd owned by mlflow_user
$ createdb -O mlflow_user mlflow_db

د سرور پیل کولو لپاره ، تاسو اړتیا لرئ لاندې د پیتون کڅوړې نصب کړئ (زه د جلا مجازی چاپیریال رامینځته کولو وړاندیز کوم):

pip install mlflow
pip install pysftp

راځئ چې خپل سرور پیل کړو

$ mlflow server  
                 --backend-store-uri postgresql://mlflow_user:mlflow@localhost/mlflow_db 
                 --default-artifact-root sftp://mlflowsftp:mlflow@sftp_host/upload  
                --host server_host 
                --port server_port

2. تعقیب اضافه کړئ

د دې لپاره چې زموږ د روزنې پایلې له لاسه ورنکړل شي، د پراختیا کونکي راتلونکي نسلونه پوه شي چې څه پیښ شوي، او د زړو ملګرو او تاسو لپاره چې د زده کړې پروسې په آرامۍ سره تحلیل شي، موږ باید تعقیب اضافه کړو. د تعقیب معنی د پیرامیټونو، میټریکونو، آثارو او د روزنې د پیل په اړه نور اضافي معلومات خوندي کول، زموږ په قضیه کې، په سرور کې.

د مثال په توګه، ما یو کوچنی جوړ کړ په ګیتوب کې پروژه په کیراس کې د هر هغه څه برخې کولو لپاره چې په کې دي د COCO ډیټاسیټ. د تعقیب اضافه کولو لپاره، ما یو فایل جوړ کړ mlflow_training.py.

دلته هغه کرښې دي چیرې چې خورا په زړه پوري شیان پیښیږي:

def run(self, epochs, lr, experiment_name):
        # getting the id of the experiment, creating an experiment in its absence
        remote_experiment_id = self.remote_server.get_experiment_id(name=experiment_name)
        # creating a "run" and getting its id
        remote_run_id = self.remote_server.get_run_id(remote_experiment_id)

        # indicate that we want to save the results on a remote server
        mlflow.set_tracking_uri(self.tracking_uri)
        mlflow.set_experiment(experiment_name)

        with mlflow.start_run(run_id=remote_run_id, nested=False):
            mlflow.keras.autolog()
            self.train_pipeline.train(lr=lr, epochs=epochs)

        try:
            self.log_tags_and_params(remote_run_id)
        except mlflow.exceptions.RestException as e:
            print(e)

دلته self.remote_server د mlflow.tracking میتودونو باندې یو کوچنی پوښ دی. MlflowClient (ما دا د اسانتیا لپاره جوړ کړی)، د کوم په مرسته چې زه یوه تجربه رامینځته کوم او په سرور کې یې پرمخ وړم. بل، زه په ګوته کوم چې د لانچ پایلې باید یوځای شي (mlflow.set_tracking_uri(self.tracking_uri)). زه د اتوماتیک لاګنګ فعالوم mlflow.keras.autolog(). اوس مهال د MLflow تعقیب د TensorFlow، Keras، Gluon XGBoost، LightGBM، Spark لپاره د اتوماتیک ننوتلو ملاتړ کوي. که تاسو خپل چوکاټ یا کتابتون نه وي موندلی، نو تاسو کولی شئ تل په ښکاره توګه ننوځئ. موږ روزنه پیل کوو. په ریموټ سرور کې ټګونه او ان پټ پیرامیټونه ثبت کړئ.

یو څو کرښې او تاسو، د هرچا په څیر، د ټولو لانچونو په اړه معلوماتو ته لاسرسی لرئ. ارام؟

3. موږ پروژه رسم کوو

اوس راځئ چې د پروژې پیل کول اسانه کړو. د دې کولو لپاره، د پروژې روټ ته د MLproject او conda.yaml فایل اضافه کړئ.
MLproject

name: flow_segmentation
conda_env: conda.yaml

entry_points:
  main:
    parameters:
        categories: {help: 'list of categories from coco dataset'}
        epochs: {type: int, help: 'number of epochs in training'}

        lr: {type: float, default: 0.001, help: 'learning rate'}
        batch_size: {type: int, default: 8}
        model_name: {type: str, default: 'Unet', help: 'Unet, PSPNet, Linknet, FPN'}
        backbone_name: {type: str, default: 'resnet18', help: 'exampe resnet18, resnet50, mobilenetv2 ...'}

        tracking_uri: {type: str, help: 'the server address'}
        experiment_name: {type: str, default: 'My_experiment', help: 'remote and local experiment name'}
    command: "python mlflow_training.py 
            --epochs={epochs}
            --categories={categories}
            --lr={lr}
            --tracking_uri={tracking_uri}
            --model_name={model_name}
            --backbone_name={backbone_name}
            --batch_size={batch_size}
            --experiment_name={experiment_name}"

د MLflow پروژه ډیری ځانګړتیاوې لري:

  • نوم - ستاسو د پروژې نوم؛
  • چاپیریال - زما په قضیه کې، conda_env په ګوته کوي چې اناکونډا د چلولو لپاره کارول کیږي او د انحصار توضیحات په conda.yaml فایل کې دي؛
  • د ننوتلو ټکي - په ګوته کوي چې کوم فایلونه او د کوم پیرامیټرو سره موږ چلولی شو (ټول پیرامیټونه په اتوماتيک ډول د روزنې پیل کولو پرمهال ننوتل کیږي)

conda.yaml

name: flow_segmentation
channels:
  - defaults
  - anaconda
dependencies:
  - python==3.7
  - pip:
    - mlflow==1.8.0
    - pysftp==0.2.9
    - Cython==0.29.19
    - numpy==1.18.4
    - pycocotools==2.0.0
    - requests==2.23.0
    - matplotlib==3.2.1
    - segmentation-models==1.0.1
    - Keras==2.3.1
    - imgaug==0.4.0
    - tqdm==4.46.0
    - tensorflow-gpu==1.14.0

تاسو کولی شئ ډاکر د خپل وخت چاپیریال په توګه وکاروئ ، د نورو توضیحاتو لپاره مهرباني وکړئ مراجعه وکړئ اسناد.

4. راځئ چې روزنه پیل کړو

موږ پروژه کلون کوو او د پروژې لارښود ته ځو:

git clone https://github.com/simbakot/mlflow_example.git
cd mlflow_example/

د چلولو لپاره تاسو اړتیا لرئ کتابتونونه نصب کړئ

pip install mlflow
pip install pysftp

ځکه په مثال کې چې زه conda_env کاروم، اناکونډا باید ستاسو په کمپیوټر کې نصب شي (مګر تاسو کولی شئ دا ټول اړین کڅوړې پخپله نصب کړئ او د لانچ پیرامیټونو سره لوبې وکړئ).

ټول چمتوالي پړاوونه بشپړ شوي او موږ کولی شو د روزنې پیل پیل کړو. د پروژې له ریښې څخه:

$ mlflow run -P epochs=10 -P categories=cat,dog -P tracking_uri=http://server_host:server_port .

کمانډ ته د ننوتلو وروسته ، د کانډا چاپیریال به په اوتومات ډول رامینځته شي او روزنه به پیل شي.
په پورته مثال کې، ما د روزنې لپاره د دورې شمیره تیره کړه، هغه کټګورۍ چې موږ یې په برخه کې غواړو (تاسو کولی شئ بشپړ لیست وګورئ دلته) او زموږ د ریموټ سرور پته.
د ممکنه پیرامیټونو بشپړ لیست د MLproject فایل کې موندل کیدی شي.

5. د زده کړې پایلې ارزونه

د روزنې بشپړولو وروسته، موږ کولی شو په براوزر کې زموږ د سرور پتې ته لاړ شو http://server_host:server_port

MLOps - د پخلي کتاب، لومړی څپرکی

دلته موږ د ټولو تجربو لیست (پورته کیڼ لور ته) او همدارنګه د منډو (منځنۍ) په اړه معلومات ګورو. موږ کولی شو د هر لانچ لپاره نور تفصيلي معلومات (پیرامیټونه، میټریکونه، آثار او ځینې اضافي معلومات) وګورو.

MLOps - د پخلي کتاب، لومړی څپرکی

د هر میټریک لپاره موږ کولی شو د بدلون تاریخ وګورو

MLOps - د پخلي کتاب، لومړی څپرکی

هغوی. په اوس وخت کې، موږ کولی شو پایلې په "دستي" حالت کې تحلیل کړو، او تاسو کولی شئ د MLflow API په کارولو سره اتوماتیک تایید هم تنظیم کړئ.

6. موډل راجستر کړئ

وروسته له دې چې موږ خپل ماډل تحلیل کړو او پریکړه یې وکړه چې دا د جګړې لپاره چمتو دی، موږ یې راجستر کوو، د دې لپاره موږ هغه لانچ غوره کوو چې موږ ورته اړتیا لرو (لکه څنګه چې په تیرو پراګراف کې ښودل شوي) او ښکته ځو.

MLOps - د پخلي کتاب، لومړی څپرکی

وروسته له دې چې موږ خپل ماډل ته نوم ورکړو، دا یوه نسخه لري. که تاسو د ورته نوم سره بل ماډل خوندي کړئ، نسخه به په اتوماتيک ډول لوړ شي.

MLOps - د پخلي کتاب، لومړی څپرکی

د هر ماډل لپاره ، موږ کولی شو توضیحات اضافه کړو او له دریو حالتونو څخه یو غوره کړو (مطالعې ، تولید ، آرشیف)؛ وروسته بیا ، د API په کارولو سره ، موږ کولی شو دې حالتونو ته لاسرسی ومومئ ، کوم چې د نسخې سره سره اضافي انعطاف چمتو کوي.

MLOps - د پخلي کتاب، لومړی څپرکی

موږ ټولو ماډلونو ته هم اسانه لاسرسی لرو

MLOps - د پخلي کتاب، لومړی څپرکی

او د هغوی نسخې

MLOps - د پخلي کتاب، لومړی څپرکی

لکه څنګه چې په تیرو پراګراف کې، ټول عملیات د API په کارولو سره ترسره کیدی شي.

7. ماډل ځای په ځای کړئ

پدې مرحله کې، موږ دمخه یو روزل شوی (کیرا) ماډل لرو. یو مثال چې تاسو یې څنګه کارولی شئ:

class SegmentationModel:
    def __init__(self, tracking_uri, model_name):

        self.registry = RemoteRegistry(tracking_uri=tracking_uri)
        self.model_name = model_name
        self.model = self.build_model(model_name)

    def get_latest_model(self, model_name):
        registered_models = self.registry.get_registered_model(model_name)
        last_model = self.registry.get_last_model(registered_models)
        local_path = self.registry.download_artifact(last_model.run_id, 'model', './')
        return local_path

    def build_model(self, model_name):
        local_path = self.get_latest_model(model_name)

        return mlflow.keras.load_model(local_path)

    def predict(self, image):
        image = self.preprocess(image)
        result = self.model.predict(image)
        return self.postprocess(result)

    def preprocess(self, image):
        image = cv2.resize(image, (256, 256))
        image = image / 255.
        image = np.expand_dims(image, 0)
        return image

    def postprocess(self, result):
        return result

دلته self.registry بیا د اسانتیا لپاره په mlflow.tracking.MlflowClient باندې یو کوچنی ریپر دی. نقطه دا ده چې زه ریموټ سرور ته لاسرسی لرم او هلته د ټاکل شوي نوم او وروستي تولید نسخه سره یو ماډل ګورم. بیا، زه په ځایی ډول د ./model فولډر ته اثار ډاونلوډ کوم او د دې لارښود څخه ماډل جوړوم mlflow.keras.load_model(local_path). اوس موږ کولی شو خپل ماډل وکاروو. د CV (ML) پراختیا کونکي کولی شي په اسانۍ سره ماډل ته وده ورکړي او نوې نسخې خپرې کړي.

په پای کې

ما یو سیسټم وړاندې کړ چې اجازه ورکوي:

  • په مرکزي توګه د ML ماډلونو، روزنې پرمختګ او پایلو په اړه معلومات ذخیره کړئ؛
  • ژر تر ژره د پراختیا چاپیریال ځای په ځای کول؛
  • په موډلونو کې د کار پرمختګ څارنه او تحلیل؛
  • دا د ماډلونو حالت نسخه او اداره کولو لپاره مناسب دی؛
  • د پایلې لرونکي ماډلونو ځای په ځای کول اسانه دي.

دا بیلګه یو لوبو دی او ستاسو د خپل سیسټم رامینځته کولو لپاره د پیل ټکي په توګه کار کوي ، پدې کې ممکن د پایلو ارزونې اتومات کول او د ماډلونو ثبت کول شامل وي (په ترتیب سره 5 او 6 ټکي) یا تاسو به د ډیټاسیټونو نسخه اضافه کړئ ، یا شاید بل څه؟ هغه ټکی چې زه یې هڅه کوم هغه دا دی چې تاسو په ټوله کې MLOps ته اړتیا لرئ، MLflow یوازې د پای لپاره یوه وسیله ده.

ولیکئ چې تاسو کومې ستونزې سره مخ شوي چې ما نه ښودلې؟
تاسو به په سیسټم کې څه اضافه کړئ ترڅو دا ستاسو اړتیاوې پوره کړي؟
تاسو د ټولو یا یوې برخې ستونزې حل کولو لپاره کومې وسیلې او لارې کاروئ؟

PS زه به یو څو لینک پریږدم:
د ګیتوب پروژه - https://github.com/simbakot/mlflow_example
MLflow - https://mlflow.org/
زما د کار بریښنالیک د پوښتنو لپاره - [ایمیل خوندي شوی]

زموږ شرکت وخت په وخت د معلوماتي ټکنالوجۍ متخصصینو لپاره مختلف پیښې کوربه کوي، د بیلګې په توګه: د جولای په 8 د ماسکو په وخت د ماسپښین په 19:00 بجو به په آنلاین بڼه د CV غونډه وي، که تاسو لیوالتیا لرئ، تاسو کولی شئ برخه واخلئ، نوم لیکنه دلته .

سرچینه: www.habr.com

Add a comment