{ "nbformat": 4, "nbformat_minor": 0, "metadata": { "colab": { "name": "lesson_12-R.ipynb", "provenance": [], "collapsed_sections": [] }, "kernelspec": { "name": "ir", "display_name": "R" }, "language_info": { "name": "R" }, "coopTranslator": { "original_hash": "fab50046ca413a38939d579f8432274f", "translation_date": "2025-09-06T14:46:04+00:00", "source_file": "4-Classification/3-Classifiers-2/solution/R/lesson_12-R.ipynb", "language_code": "sr" } }, "cells": [ { "cell_type": "markdown", "metadata": { "id": "jsFutf_ygqSx" }, "source": [ "# Направите класификациони модел: Укусна азијска и индијска јела\n" ] }, { "cell_type": "markdown", "metadata": { "id": "HD54bEefgtNO" }, "source": [ "## Класификатори кухиња 2\n", "\n", "У овом другом часу о класификацији, истражићемо `више начина` за класификацију категоријских података. Такође ћемо научити о последицама избора једног класификатора уместо другог.\n", "\n", "### [**Квиз пре предавања**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/23/)\n", "\n", "### **Предуслови**\n", "\n", "Претпостављамо да сте завршили претходне лекције, јер ћемо наставити са неким концептима које смо раније научили.\n", "\n", "За ову лекцију биће нам потребни следећи пакети:\n", "\n", "- `tidyverse`: [tidyverse](https://www.tidyverse.org/) је [збирка R пакета](https://www.tidyverse.org/packages) дизајнирана да учини науку о подацима бржом, лакшом и забавнијом!\n", "\n", "- `tidymodels`: [tidymodels](https://www.tidymodels.org/) је [оквир](https://www.tidymodels.org/packages/) који обухвата пакете за моделирање и машинско учење.\n", "\n", "- `themis`: [themis пакет](https://themis.tidymodels.org/) пружа додатне кораке за рецепте који се баве небалансираним подацима.\n", "\n", "Можете их инсталирати на следећи начин:\n", "\n", "`install.packages(c(\"tidyverse\", \"tidymodels\", \"kernlab\", \"themis\", \"ranger\", \"xgboost\", \"kknn\"))`\n", "\n", "Алтернативно, скрипта испод проверава да ли имате потребне пакете за завршетак овог модула и инсталира их ако недостају.\n" ] }, { "cell_type": "code", "metadata": { "id": "vZ57IuUxgyQt" }, "source": [ "suppressWarnings(if (!require(\"pacman\"))install.packages(\"pacman\"))\n", "\n", "pacman::p_load(tidyverse, tidymodels, themis, kernlab, ranger, xgboost, kknn)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "z22M-pj4g07x" }, "source": [ "## **1. Мапа класификације**\n", "\n", "У нашој [претходној лекцији](https://github.com/microsoft/ML-For-Beginners/tree/main/4-Classification/2-Classifiers-1), покушали смо да одговоримо на питање: како да изаберемо између више модела? У великој мери, то зависи од карактеристика података и типа проблема који желимо да решимо (на пример, класификација или регресија?).\n", "\n", "Раније смо научили о различитим опцијама које имате када класификујете податке користећи Microsoft-ов подсетник. Python-ов оквир за машинско учење, Scikit-learn, нуди сличан, али детаљнији подсетник који може додатно помоћи у сужењу избора проценитеља (други термин за класификаторе):\n", "\n", "

\n", " \n", "

\n" ] }, { "cell_type": "markdown", "metadata": { "id": "u1i3xRIVg7vG" }, "source": [ "> Савет: [погледајте ову мапу онлајн](https://scikit-learn.org/stable/tutorial/machine_learning_map/) и кликните дуж путање да бисте прочитали документацију. \n", "> \n", "> [Референтни сајт за Tidymodels](https://www.tidymodels.org/find/parsnip/#models) такође пружа одличну документацију о различитим типовима модела.\n", "\n", "### **План** 🗺️\n", "\n", "Ова мапа је веома корисна када имате јасно разумевање ваших података, јер можете „шетати“ дуж њених путања до доношења одлуке:\n", "\n", "- Имамо више од 50 узорака\n", "\n", "- Желимо да предвидимо категорију\n", "\n", "- Имамо означене податке\n", "\n", "- Имамо мање од 100.000 узорака\n", "\n", "- ✨ Можемо изабрати Linear SVC\n", "\n", "- Ако то не функционише, пошто имамо нумеричке податке\n", "\n", " - Можемо пробати ✨ KNeighbors Classifier\n", "\n", " - Ако ни то не функционише, пробајте ✨ SVC и ✨ Ensemble Classifiers\n", "\n", "Ово је веома корисна путања коју треба пратити. Сада, хајде да одмах пређемо на то користећи [tidymodels](https://www.tidymodels.org/) оквир за моделирање: конзистентну и флексибилну колекцију R пакета развијених да подстакну добру статистичку праксу 😊.\n", "\n", "## 2. Поделите податке и решите проблем неуравнотеженог скупа података.\n", "\n", "Из претходних лекција смо научили да постоји скуп заједничких састојака у нашим кухињама. Такође, постојала је прилично неравномерна расподела у броју кухиња.\n", "\n", "Ово ћемо решити на следећи начин:\n", "\n", "- Избацивањем најчешћих састојака који стварају конфузију између различитих кухиња, користећи `dplyr::select()`.\n", "\n", "- Коришћењем `recipe` који претходно обрађује податке како би их припремио за моделирање применом алгоритма `over-sampling`.\n", "\n", "Ово смо већ обрадили у претходној лекцији, тако да би ово требало да буде лако 🥳!\n" ] }, { "cell_type": "code", "metadata": { "id": "6tj_rN00hClA" }, "source": [ "# Load the core Tidyverse and Tidymodels packages\n", "library(tidyverse)\n", "library(tidymodels)\n", "\n", "# Load the original cuisines data\n", "df <- read_csv(file = \"https://raw.githubusercontent.com/microsoft/ML-For-Beginners/main/4-Classification/data/cuisines.csv\")\n", "\n", "# Drop id column, rice, garlic and ginger from our original data set\n", "df_select <- df %>% \n", " select(-c(1, rice, garlic, ginger)) %>%\n", " # Encode cuisine column as categorical\n", " mutate(cuisine = factor(cuisine))\n", "\n", "\n", "# Create data split specification\n", "set.seed(2056)\n", "cuisines_split <- initial_split(data = df_select,\n", " strata = cuisine,\n", " prop = 0.7)\n", "\n", "# Extract the data in each split\n", "cuisines_train <- training(cuisines_split)\n", "cuisines_test <- testing(cuisines_split)\n", "\n", "# Display distribution of cuisines in the training set\n", "cuisines_train %>% \n", " count(cuisine) %>% \n", " arrange(desc(n))" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "zFin5yw3hHb1" }, "source": [ "### Рад са неуравнотеженим подацима\n", "\n", "Неуравнотежени подаци често имају негативан утицај на перформансе модела. Многи модели најбоље функционишу када је број опсервација једнак и, самим тим, имају потешкоћа са неуравнотеженим подацима.\n", "\n", "Постоје два главна начина за рад са неуравнотеженим скуповима података:\n", "\n", "- додавање опсервација мањинској класи: `Прекомерно узорковање` (Over-sampling), на пример, коришћењем SMOTE алгоритма који синтетички генерише нове примере мањинске класе користећи најближе суседе тих случајева.\n", "\n", "- уклањање опсервација из већинске класе: `Потцењено узорковање` (Under-sampling)\n", "\n", "У претходном часу, демонстрирали смо како да радимо са неуравнотеженим скуповима података користећи `рецепт`. Рецепт се може сматрати планом који описује које кораке треба применити на скуп података како би био спреман за анализу података. У нашем случају, желимо да имамо једнаку дистрибуцију броја наших кухиња за наш `тренинг скуп`. Хајде да почнемо.\n" ] }, { "cell_type": "code", "metadata": { "id": "cRzTnHolhLWd" }, "source": [ "# Load themis package for dealing with imbalanced data\n", "library(themis)\n", "\n", "# Create a recipe for preprocessing training data\n", "cuisines_recipe <- recipe(cuisine ~ ., data = cuisines_train) %>%\n", " step_smote(cuisine) \n", "\n", "# Print recipe\n", "cuisines_recipe" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "KxOQ2ORhhO81" }, "source": [ "Сада смо спремни да обучавамо моделе 👩‍💻👨‍💻!\n", "\n", "## 3. Изван модела мултиномијалне регресије\n", "\n", "У претходној лекцији, разматрали смо моделе мултиномијалне регресије. Хајде да истражимо неке флексибилније моделе за класификацију.\n", "\n", "### Машине за подршку векторима\n", "\n", "У контексту класификације, `Машине за подршку векторима` су техника машинског учења која настоји да пронађе *хиперплан* који \"најбоље\" раздваја класе. Хајде да погледамо једноставан пример:\n", "\n", "

\n", " \n", "

https://commons.wikimedia.org/w/index.php?curid=22877598
\n" ] }, { "cell_type": "markdown", "metadata": { "id": "C4Wsd0vZhXYu" }, "source": [ "H1~ не раздваја класе. H2~ их раздваја, али само са малом маргином. H3~ их раздваја са максималном маргином.\n", "\n", "#### Линеарни класификатор подршке векторима\n", "\n", "Кластерисање подршке векторима (SVC) је део породице техника машинског учења заснованих на машинама подршке векторима. У SVC-у, хиперплан се бира тако да правилно раздвоји `већину` посматрања из тренинг скупа, али `може погрешно класификовати` нека посматрања. Дозвољавањем да неке тачке буду на погрешној страни, SVM постаје отпорнији на изузетке, чиме се побољшава генерализација на нове податке. Параметар који регулише ово кршење назива се `cost`, који има подразумевану вредност 1 (погледајте `help(\"svm_poly\")`).\n", "\n", "Хајде да направимо линеарни SVC тако што ћемо поставити `degree = 1` у полиномском SVM моделу.\n" ] }, { "cell_type": "code", "metadata": { "id": "vJpp6nuChlBz" }, "source": [ "# Make a linear SVC specification\n", "svc_linear_spec <- svm_poly(degree = 1) %>% \n", " set_engine(\"kernlab\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle specification and recipe into a worklow\n", "svc_linear_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(svc_linear_spec)\n", "\n", "# Print out workflow\n", "svc_linear_wf" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "rDs8cWNkhoqu" }, "source": [ "Сада када смо обухватили кораке предобраде и спецификацију модела у *workflow*-у, можемо наставити са тренингом линеарног SVC-а и проценом резултата у истом процесу. За метрике перформанси, хајде да направимо сет метрика који ће процењивати: `тачност`, `осетљивост`, `позитивну предиктивну вредност` и `F меру`.\n", "\n", "> `augment()` ће додати колону(е) за предикције у дате податке.\n" ] }, { "cell_type": "code", "metadata": { "id": "81wiqcwuhrnq" }, "source": [ "# Train a linear SVC model\n", "svc_linear_fit <- svc_linear_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "# Create a metric set\n", "eval_metrics <- metric_set(ppv, sens, accuracy, f_meas)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "svc_linear_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "0UFQvHf-huo3" }, "source": [ "#### Машина за подршку вектора\n", "\n", "Машина за подршку вектора (SVM) је проширење класификатора за подршку вектора како би се омогућила нелинеарна граница између класа. У суштини, SVM користи *трик са језгром* да прошири простор карактеристика и прилагоди се нелинеарним односима између класа. Једна популарна и изузетно флексибилна функција језгра коју SVM користи је *функција радијалне основе.* Хајде да видимо како ће се она показати на нашим подацима.\n" ] }, { "cell_type": "code", "metadata": { "id": "-KX4S8mzhzmp" }, "source": [ "set.seed(2056)\n", "\n", "# Make an RBF SVM specification\n", "svm_rbf_spec <- svm_rbf() %>% \n", " set_engine(\"kernlab\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle specification and recipe into a worklow\n", "svm_rbf_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(svm_rbf_spec)\n", "\n", "\n", "# Train an RBF model\n", "svm_rbf_fit <- svm_rbf_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "svm_rbf_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "QBFSa7WSh4HQ" }, "source": [ "Много боље 🤩!\n", "\n", "> ✅ Молимо вас да погледате:\n", ">\n", "> - [*Support Vector Machines*](https://bradleyboehmke.github.io/HOML/svm.html), Практично машинско учење са R\n", ">\n", "> - [*Support Vector Machines*](https://www.statlearning.com/), Увод у статистичко учење са апликацијама у R\n", ">\n", "> за додатно читање.\n", "\n", "### Класификатори најближих суседа\n", "\n", "*K*-најближи сусед (KNN) је алгоритам у којем се свака опсервација предвиђа на основу њене *сличности* са другим опсервацијама.\n", "\n", "Хајде да га применимо на наше податке.\n" ] }, { "cell_type": "code", "metadata": { "id": "k4BxxBcdh9Ka" }, "source": [ "# Make a KNN specification\n", "knn_spec <- nearest_neighbor() %>% \n", " set_engine(\"kknn\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle recipe and model specification into a workflow\n", "knn_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(knn_spec)\n", "\n", "# Train a boosted tree model\n", "knn_wf_fit <- knn_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "knn_wf_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "HaegQseriAcj" }, "source": [ "Чини се да овај модел не ради баш најбоље. Вероватно ће промена аргумената модела (погледајте `help(\"nearest_neighbor\")`) побољшати перформансе модела. Обавезно пробајте.\n", "\n", "> ✅ Погледајте:\n", ">\n", "> - [Hands-on Machine Learning with R](https://bradleyboehmke.github.io/HOML/)\n", ">\n", "> - [An Introduction to Statistical Learning with Applications in R](https://www.statlearning.com/)\n", ">\n", "> да бисте сазнали више о класификаторима *K*-најближих суседа.\n", "\n", "### Енсембл класификатори\n", "\n", "Енсембл алгоритми функционишу тако што комбинују више основних проценитеља како би произвели оптималан модел, било кроз:\n", "\n", "`bagging`: примену *функције просека* на колекцију основних модела\n", "\n", "`boosting`: изградњу секвенце модела који се надовезују један на други ради побољшања предиктивних перформанси.\n", "\n", "Хајде да започнемо са испробавањем модела Случајне шуме (Random Forest), који гради велику колекцију одлуковних стабала, а затим примењује функцију просека ради добијања бољег укупног модела.\n" ] }, { "cell_type": "code", "metadata": { "id": "49DPoVs6iK1M" }, "source": [ "# Make a random forest specification\n", "rf_spec <- rand_forest() %>% \n", " set_engine(\"ranger\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle recipe and model specification into a workflow\n", "rf_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(rf_spec)\n", "\n", "# Train a random forest model\n", "rf_wf_fit <- rf_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "rf_wf_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "RGVYwC_aiUWc" }, "source": [ "Добар посао 👏!\n", "\n", "Хајде да експериментишемо са моделом Boosted Tree.\n", "\n", "Boosted Tree представља метод ансамбла који креира серију секвенцијалних одлука стабала где свако стабло зависи од резултата претходних стабала у настојању да постепено смањи грешку. Фокусира се на тежине погрешно класификованих ставки и прилагођава модел за следећи класификатор како би исправио грешке.\n", "\n", "Постоје различити начини за подешавање овог модела (погледајте `help(\"boost_tree\")`). У овом примеру, подешаваћемо Boosted стабла преко `xgboost` механизма.\n" ] }, { "cell_type": "code", "metadata": { "id": "Py1YWo-micWs" }, "source": [ "# Make a boosted tree specification\n", "boost_spec <- boost_tree(trees = 200) %>% \n", " set_engine(\"xgboost\") %>% \n", " set_mode(\"classification\")\n", "\n", "# Bundle recipe and model specification into a workflow\n", "boost_wf <- workflow() %>% \n", " add_recipe(cuisines_recipe) %>% \n", " add_model(boost_spec)\n", "\n", "# Train a boosted tree model\n", "boost_wf_fit <- boost_wf %>% \n", " fit(data = cuisines_train)\n", "\n", "\n", "# Make predictions and Evaluate model performance\n", "boost_wf_fit %>% \n", " augment(new_data = cuisines_test) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class)" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "zNQnbuejigZM" }, "source": [ "> ✅ Молимо вас да погледате:\n", ">\n", "> - [Машинско учење за друштвене научнике](https://cimentadaj.github.io/ml_socsci/tree-based-methods.html#random-forests)\n", ">\n", "> - [Практично машинско учење са R](https://bradleyboehmke.github.io/HOML/)\n", ">\n", "> - [Увод у статистичко учење са апликацијама у R](https://www.statlearning.com/)\n", ">\n", "> - - Истражује модел AdaBoost који је добра алтернатива за xgboost.\n", ">\n", "> за више информација о класификаторима ансамбла.\n", "\n", "## 4. Додатно - поређење више модела\n", "\n", "У овом лабораторијском раду смо применили прилично велики број модела 🙌. Може постати заморно или напорно креирати много радних токова из различитих сетова претпроцесора и/или спецификација модела, а затим израчунати метрике перформанси једну по једну.\n", "\n", "Хајде да видимо да ли можемо да решимо ово креирањем функције која примењује листу радних токова на сет за обуку, а затим враћа метрике перформанси на основу тест сета. Користићемо `map()` и `map_dfr()` из пакета [purrr](https://purrr.tidyverse.org/) да применимо функције на сваки елемент у листи.\n", "\n", "> [`map()`](https://purrr.tidyverse.org/reference/map.html) функције вам омогућавају да замените многе for петље кодом који је и сажетији и лакши за читање. Најбоље место за учење о [`map()`](https://purrr.tidyverse.org/reference/map.html) функцијама је [поглавље о итерацији](http://r4ds.had.co.nz/iteration.html) у R за науку о подацима.\n" ] }, { "cell_type": "code", "metadata": { "id": "Qzb7LyZnimd2" }, "source": [ "set.seed(2056)\n", "\n", "# Create a metric set\n", "eval_metrics <- metric_set(ppv, sens, accuracy, f_meas)\n", "\n", "# Define a function that returns performance metrics\n", "compare_models <- function(workflow_list, train_set, test_set){\n", " \n", " suppressWarnings(\n", " # Fit each model to the train_set\n", " map(workflow_list, fit, data = train_set) %>% \n", " # Make predictions on the test set\n", " map_dfr(augment, new_data = test_set, .id = \"model\") %>%\n", " # Select desired columns\n", " select(model, cuisine, .pred_class) %>% \n", " # Evaluate model performance\n", " group_by(model) %>% \n", " eval_metrics(truth = cuisine, estimate = .pred_class) %>% \n", " ungroup()\n", " )\n", " \n", "} # End of function" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "Fwa712sNisDA" }, "source": [] }, { "cell_type": "code", "metadata": { "id": "3i4VJOi2iu-a" }, "source": [ "# Make a list of workflows\n", "workflow_list <- list(\n", " \"svc\" = svc_linear_wf,\n", " \"svm\" = svm_rbf_wf,\n", " \"knn\" = knn_wf,\n", " \"random_forest\" = rf_wf,\n", " \"xgboost\" = boost_wf)\n", "\n", "# Call the function\n", "set.seed(2056)\n", "perf_metrics <- compare_models(workflow_list = workflow_list, train_set = cuisines_train, test_set = cuisines_test)\n", "\n", "# Print out performance metrics\n", "perf_metrics %>% \n", " group_by(.metric) %>% \n", " arrange(desc(.estimate)) %>% \n", " slice_head(n=7)\n", "\n", "# Compare accuracy\n", "perf_metrics %>% \n", " filter(.metric == \"accuracy\") %>% \n", " arrange(desc(.estimate))\n" ], "execution_count": null, "outputs": [] }, { "cell_type": "markdown", "metadata": { "id": "KuWK_lEli4nW" }, "source": [ "Пакет [**workflowset**](https://workflowsets.tidymodels.org/) омогућава корисницима да креирају и лако прилагоде велики број модела, али је углавном дизајниран за рад са техникама ресемплирања као што је `крстална валидација`, приступ који тек треба да обрадимо.\n", "\n", "## **🚀Изазов**\n", "\n", "Свака од ових техника има велики број параметара које можете прилагодити, на пример `cost` у SVM-у, `neighbors` у KNN-у, `mtry` (случајно изабрани предиктори) у Random Forest-у.\n", "\n", "Истражите подразумеване вредности параметара за сваки модел и размислите шта би значило прилагођавање ових параметара за квалитет модела.\n", "\n", "Да бисте сазнали више о одређеном моделу и његовим параметрима, користите: `help(\"model\")`, на пример `help(\"rand_forest\")`.\n", "\n", "> У пракси, обично *процењујемо* *најбоље вредности* за ове параметре тако што тренирамо многе моделе на `симулираном скупу података` и меримо колико добро ти модели раде. Овај процес се назива **тјунинг**.\n", "\n", "### [**Квиз након предавања**](https://gray-sand-07a10f403.1.azurestaticapps.net/quiz/24/)\n", "\n", "### **Преглед и самостално учење**\n", "\n", "У овим лекцијама има доста стручних термина, па одвојите минут да прегледате [ову листу](https://docs.microsoft.com/dotnet/machine-learning/resources/glossary?WT.mc_id=academic-77952-leestott) корисне терминологије!\n", "\n", "#### ХВАЛА:\n", "\n", "[`Allison Horst`](https://twitter.com/allison_horst/) за креирање невероватних илустрација које чине R приступачнијим и занимљивијим. Пронађите више илустрација у њеној [галерији](https://www.google.com/url?q=https://github.com/allisonhorst/stats-illustrations&sa=D&source=editors&ust=1626380772530000&usg=AOvVaw3zcfyCizFQZpkSLzxiiQEM).\n", "\n", "[Cassie Breviu](https://www.twitter.com/cassieview) и [Jen Looper](https://www.twitter.com/jenlooper) за креирање оригиналне Python верзије овог модула ♥️\n", "\n", "Срећно учење,\n", "\n", "[Eric](https://twitter.com/ericntay), Златни амбасадор студената Microsoft Learn.\n", "\n", "

\n", " \n", "

Илустрација од @allison_horst
\n" ] }, { "cell_type": "markdown", "metadata": {}, "source": [ "\n---\n\n**Одрицање од одговорности**: \nОвај документ је преведен коришћењем услуге за превођење помоћу вештачке интелигенције [Co-op Translator](https://github.com/Azure/co-op-translator). Иако тежимо тачности, молимо вас да имате у виду да аутоматски преводи могу садржати грешке или нетачности. Оригинални документ на изворном језику треба сматрати ауторитативним извором. За критичне информације препоручује се професионални превод од стране људи. Не сносимо одговорност за било каква погрешна тумачења или неспоразуме који могу произаћи из коришћења овог превода.\n" ] } ] }