Машинне навчання · Блок 8 · Тема 40

Інтерпретованість моделей

Модель позначила оголошення шахрайським. Пояснити чому — не те саме, що зрозуміти чому: пояснення описує модель, а не світ. Різниця між цими двома реченнями і є вся тема.

На дошці оголошень про вживані телефони випадковий ліс позначив конкретне оголошення шахрайським: Beta 12 Pro 2017 року, ціна 1050 грн, акаунт продавця живе 73 дні. Модель дає 0.72. Продавець пише в підтримку: чому? Відповісти «так вирішив алгоритм» не можна. Відповісти «бо ціна занизька» вже краще — але звідки ти знаєш, що модель дивилась саме на ціну?

Ліс із двохсот дерев не читається вголос, як одне дерево. Він дає точність 0.9133 і AUC 0.9586 там, де дерево глибини три дає 0.7155 — і мовчить про свої мотиви. Уся ця тема про те, як усе-таки змусити його говорити, і — що важливіше — наскільки можна вірити сказаному.

01 / МотивЧотири різні «навіщо»

Слово «пояснити» ховає щонайменше чотири різні задачі. Їх постійно плутають, і саме через це суперечки про інтерпретованість заходять у глухий кут: люди хочуть різного, а слово вживають одне.

Довіра користувача. Продавцю з дошки байдужі рейтинги ознак. Йому потрібне речення про його оголошення й, бажано, дія: що змінити, щоб рішення змінилось. Це локальне пояснення одного рядка.

Налагодження моделі. Інженеру потрібне протилежне — карта того, звідки модель бере сигнал загалом. Якщо виявиться, що головна ознака — технічний ідентифікатор запису, модель зламана, і краще дізнатись про це до запуску.

Вимоги регулятора. Аудитору потрібне пояснення відтворюване: таке, що завтра на тому самому рядку дасть те саме, і таке, що можна записати в документ. Тут часто виграє не найкращий метод пояснення, а найпростіша модель — її пояснення й є вона сама.

Наукове розуміння. Дослідник хоче знати, як улаштований світ: чи справді свіжий акаунт спричиняє шахрайство. І ось тут погана новина, яку ми розгорнемо в розділі 8: жоден метод із цієї теми на таке питання не відповідає. Вони всі описують модель, а не світ.

Схема 1 · Чотири причини — чотири різні пояснення

Перш ніж обирати метод, дай відповідь: хто питає й що він робитиме з відповіддю.

Головне: метод обирають не за модою, а за питанням. Локальне пояснення нічого не скаже про модель загалом, глобальний рейтинг ознак нічого не скаже про конкретного продавця, а причину не дає жоден із них.

02 / РозвилкаДві родини пояснень

Далі всі способи діляться на дві родини, і різниця між ними принципова.

Перша: модель зрозуміла за будовою. Лінійна регресія — це список коефіцієнтів; дерево — це список питань. Пояснення не треба будувати, воно вже є, і воно точне: це буквально те, що модель робить.

Ось наша логістична регресія на шести ознаках дошки, коефіцієнти при стандартизованих ознаках: ціна +0.105, ціна нового −0.215, рік +0.089, стан +0.068, памʼять +0.174, вік акаунта −0.416. Читається вголос за секунду: чим старший акаунт, тим спокійніше; чим дорожче оголошення, тим підозріліше.

Друга половина цього речення — неправда. Три чверті шахраїв на нашій дошці ціну занижують. Модель не збрехала: вона чесно показала своє правило, а правило погане, бо звʼязок ціни з шахрайством не монотонний, а лінійна модель іншого не вміє. Її AUC — 0.5988, майже підкидання монети. Прозорість не гарантує правильності. Вона гарантує лише те, що помилку видно.

Дерево глибини три впоралось краще: перший розріз ціна <= 735 — це і є приманка на жадібність, AUC 0.7155. Але до лісу з його 0.9586 обом далеко. Тема 20 назвала це компромісом точності й зрозумілості; тут ми бачимо його ціною в 0.24 AUC.

Друга родина: пояснення, надбудоване над чорною скринькою. Модель лишається складною, а поруч будується інша, спрощена модель — модель поведінки першої. І ось тут причаїлась головна пастка теми: спрощена модель поведінки не зобовʼязана збігатися з поведінкою. Вона наближає її — де краще, де гірше.

03 / МасштабГлобальне проти локального

Друге розрізнення — про масштаб питання. «Які ознаки важливі для моделі загалом» і «чому вона так вирішила про це оголошення» — різні питання, і відповіді на них не зводяться одна до одної.

Глобальна відповідь — це середнє по тисячі рядків. Конкретний рядок не зобовʼязаний бути схожим на середнє, як конкретна людина не зобовʼязана мати 1.7 дитини. На нашому лісі глобальний рейтинг очолює ціна (важливість 0.5444), а памʼять пасе задніх — пʼята з шести з важливістю 0.0435.

А тепер візьми оголошення 97: Alfa A7 2022 року, нове, 512 ГБ, ціна 8240 грн при типовій ціні нового 7400. Модель дає 0.3655 — помітно вище за середні 0.1453 по дошці, хоч до порога й не дотягує. Найбільший внесок у це підвищення дає памʼять (+0.0848), тобто ознака, яка глобально майже нічого не важить. А ціна — головна ознака моделі — тягне прогноз униз (−0.0327).

Інтерактив 1 · Той самий ліс, чотири оголошення

Ліворуч — глобальна важливість ознак, вона одна на всю модель. Праворуч — внески в прогноз саме цього оголошення (значення Шеплі, точний розрахунок).

оголошення з дошки, ціна в гривнях
прогноз
база по дошці
вирішує тут
її місце глобально
Прожени всі чотири. У першому оголошенні локальне пояснення збігається з глобальним рейтингом, у четвертому — розходиться повністю: вирішує пʼята за важливістю ознака, а перша працює проти. Обидва пояснення правильні, бо відповідають на різні питання.

04 / ПасткиВажливість ознак і її пастки

Найпоширеніший спосіб пояснення — рейтинг важливості ознак. У темі 26 ми вже познайомились із двома його різновидами: MDI (те, що sklearn віддає у feature_importances_: сума приростів чистоти по всіх розрізах ознаки) і перестановкова важливість (падіння якості на нових даних після перемішування колонки). Там сказано, що вони міряють різне. Тут подивимось, наскільки різне, — на числах.

ознакаMDIмісцеперестановковамісце
ціна0.544410.40431
вік акаунта0.178520.01434
ціна нового0.103430.14892
рік0.100440.11363
памʼять0.043550.01035
стан0.029660.00526

Найцікавіший рядок — вік акаунта. За MDI це друга ознака моделі, за перестановковою — четверта, і майже нуль. Помилки тут немає в жодного з методів: ми самі будували дані так, що шанс шахрайства залежить від віку акаунта, тобто вік — справжня причина. Але шахрай ще й ставить дивну ціну, і ціна видає його краще. Прибери вік акаунта — модель майже нічого не втратить. Важливість міряє корисність ознаки для моделі, а не її роль у світі.

Дослід із випадковою колонкою

Тепер найпереконливіший дослід теми. Додамо до таблиці колонку код_оголошення — чисте випадкове число, яке за побудовою не знає нічого. І будемо міняти в ній рівно одне: скільки в неї різних значень.

Інтерактив 2 · Скільки коштує велика кардинальність

Сім ознак, з яких сьома — випадковий код. Ліс на 120 дерев перенавчається заново для кожного положення повзунка.

MDI коду
місце з семи
перестановкова
місце з семи
Прожени всі сім положень. Колонка з нульовою інформацією при двох значеннях чесно стоїть останньою (MDI 0.0166). При 1200 значеннях MDI піднімає її на третє місце з семи (0.1207) — вище за «ціну нового» і «рік», які справді щось знають. Перестановкова важливість не купується: сьоме місце на всіх семи положеннях.

Механізм простий. Чим більше в ознаки різних значень, тим більше в дерева кандидатів на поріг, і тим імовірніше, що хоч один із них випадково відріже трохи забрудненості на навчальних даних. MDI цю випадковість чесно оплачує — вона й рахується на навчальних даних. Звідси перше практичне правило: ідентифікатори, коди й будь-які майже-унікальні колонки роблять рейтинг MDI непридатним.

Практичний висновок для щоденної роботи: feature_importances_ зручно дивитись першим — воно безкоштовне, — але жодне рішення на ньому одному не приймають. Рішення приймають за перестановковою важливістю, порахованою на відкладеній вибірці, і бажано з кількома повторами: перемішування випадкове, тож одне число ще нічого не означає.

А перестановкова ламається на близнюках

Перестановкова важливість чесніша, але має власну слабкість. Уяви, що дошка зберігає ціну ще й у доларах, окремою колонкою. Нової інформації нуль, кореляція одиниця.

Схема 2 · Що робить із важливістю колонка-близнюк

Падіння AUC від перемішування колонки. Ті самі дані, той самий ліс — різниця лише в тому, чи є в таблиці друга копія ціни.

Що читати: сама по собі ціна коштує 0.4043 AUC. Варто додати її копію — і кожна з двох колонок «дешевшає» до 0.1429 і 0.1180: коли ми псуємо одну, модель бере те саме з другої. А перемішані разом вони забирають 0.4346 — більше, ніж сума їхніх окремих важливостей (0.2609). Важливість не ділиться на доданки.
Практичний наслідок. Три-чотири схожі колонки цілком можуть опуститися нижче порога, за яким ознаки відкидають як непотрібні, — і ти викинеш усю групу, яка насправді була головною. Перед тим як щось відкидати за важливістю, подивись на кореляційну матрицю й перемішуй корельовані колонки групами.

05 / ФормаЧастинна залежність: PDP і ICE

Рейтинг каже, які ознаки важливі, але не каже, як вони діють. На це відповідає частинна залежність (partial dependence plot, PDP). Рецепт прямолінійний: щоб дізнатись прогноз при ціні 5000 грн, постав ціну 5000 усім рядкам навчальної вибірки, порахуй прогнози й усередни їх. Потім те саме для інших значень.

PDP(v) = середнє по всіх рядках від f(рядок із заміненою ознакою = v)

На нашому лісі крива виходить у формі ванни: 0.8762 при ціні 500 грн, мінімум 0.0923 на 5000, і знову підйом до 0.4156 на 32 000. Обидві приманки видно на одній картинці — занижена ціна ловить на жадібності, завищена йде під велику передоплату.

Схема 3 · Одна середня крива й чотири справжні

Товста лінія — PDP по 900 навчальних рядках. Тонкі — ICE: та сама процедура, але для одного оголошення кожна.

Що читати: дно ванни в кожного своє. У дешевого Alfa A5 прогноз мінімальний біля 3000 грн (0.057), у флагмана Beta 12 Pro — аж біля 10 000 (0.117). Середня крива проходить між ними й не описує жодного. Якщо ICE-криві розходяться так сильно, PDP читати вже небезпечно.

І є ще одна незручність, важливіша за першу. Щоб порахувати PDP у точці 32 000 грн, ми поставили ціну 32 000 усім рядкам — включно з тими, де йдеться про Alfa A5, чия ціна нового 5200. На всій дошці жодного оголошення Alfa A5 дорожче за 20 000 грн немає, і взагалі найдорожчий Alfa A5 коштує 13 310 грн. Модель у таких точках нічого не вчила. Вона там просто щось видає — і це «щось» потрапляє в середнє.

Схема 4 · Де PDP питає модель про те, чого не бувало

Смуги — діапазон цін, які реально трапляються на дошці для кожної моделі телефона. Пунктир — точка 32 000 грн, у якій PDP усереднює прогноз по всіх рядках.

Що читати: пунктир перетинає лише три смуги з шести. Для 552 оголошень із 1200 — усі Alfa A5 і Alfa A7 — ціна 32 000 грн лежить далеко за межами всього, що модель бачила. PDP усе одно бере їхні прогнози в середнє з повною вагою.

Ліки часткові, але дешеві: малюй ICE поверх PDP і не довіряй ділянкам кривої, де навчальних даних мало. Гістограма ознаки під графіком PDP — найкорисніша прикраса, яку можна до нього додати.

06 / СусідLIME одним абзацом

LIME (local interpretable model-agnostic explanations) підходить до локального пояснення з іншого боку. Навколо потрібної точки він генерує хмару схожих рядків, питає в чорної скриньки прогноз для кожного, а потім навчає на цій хмарі просту зважену лінійну модель — ближчі точки важать більше. Коефіцієнти цієї маленької моделі й подаються як пояснення: мовляв, отут, поруч із твоєм оголошенням, велика модель поводиться приблизно так. Ідея гарна й дуже наочна. Головна вада — нестабільність: хмара генерується випадково, і два запуски на тому самому рядку цілком можуть дати різні, іноді суперечливі пояснення. Плюс доводиться вибирати радіус хмари, а від нього відповідь залежить сильно. Для розмови з регулятором, якому потрібне відтворюване пояснення, це дискваліфікація.

Сусід поруч: «що змінити»

Є ще один спосіб пояснити рішення, і продавцю з дошки він потрібен найбільше — контрфактне пояснення (counterfactual). Замість «яка ознака скільки важила» воно відповідає на питання «що мінімально змінити, щоб рішення стало іншим»: «якби ціна була не 1050, а 2400 грн, оголошення не потрапило б у підозрілі». Шукають таку відповідь перебором або оптимізацією: серед усіх змін, які перевертають рішення, беруть найдешевшу й таку, що фізично можлива (вік акаунта не зменшиш). Плюс очевидний — з відповіді одразу видно дію. Мінус теж: контрфакт описує межу рішень моделі, а не причину, і кількох різних контрфактів для одного рядка зазвичай знаходиться багато.

07 / ГоловнеSHAP: чесний поділ виграшу

SHAP розвʼязує ту саму задачу, але з іншого кінця — з теорії кооперативних ігор. Уяви, що прогноз — це виграш команди, а ознаки — гравці, які його здобули. Питання: як поділити виграш між гравцями чесно?

У 1953 році Ллойд Шеплі показав, що якщо вимагати кількох очевидних властивостей (гравець, який нічого не додає, отримує нуль; однакові гравці отримують порівну; частки складаються у весь виграш), то спосіб поділу існує рівно один. Він зветься значенням Шеплі, і рецепт такий: перебрати всі порядки, у яких гравці могли заходити в команду, у кожному порядку записати приріст від приходу гравця, і усереднити.

Що таке «команда» для ознак. Гравці в грі приходять і йдуть, а ознаки в моделі є завжди — модель не вміє працювати з половиною колонок. Тому «команду S» імітують: ознакам із S дають справжні значення нашого оголошення, а решті — значення з випадкових рядків дошки, і прогноз усереднюють. Виходить «що знає модель, якщо їй сказали лише S». Цей набір випадкових рядків зветься фоном.

Рахуємо руками

Візьмімо оголошення з першого абзацу теми — Beta 12 Pro за 1050 грн з акаунта віком 73 дні — і модель на трьох ознаках: ціна, ціна нового, вік акаунта. Фон — сто випадкових оголошень із навчальної частини. Три ознаки дають вісім команд, і ось скільки коштує кожна:

команда Sv(S)команда Sv(S)
нічого0.1402ціна + нового0.6547
ціна0.4359ціна + акаунт0.5669
нового0.2851нового + акаунт0.3863
акаунт0.2152усі три0.8223

v(нічого) = 0.1402 — це середній прогноз по фону, тобто що модель думає про випадкове оголошення з дошки, нічого про нього не знаючи. v(усі три) = 0.8223 — прогноз для нашого оголошення. Він відрізняється від 0.72 з початку теми, і це не помилка: там був ліс на шести ознаках, тут — на трьох. Ділити треба різницю: 0.6821.

Інтерактив 3 · Усі шість порядків, крок за кроком

Кожен рядок — один порядок приходу ознак. Довжина відрізка — приріст цінності від приходу цієї ознаки. Сума в кожному рядку однакова: 0.6821.

крок0 / 18
ціна
ціна нового
вік акаунта
база + внески
Дійди до кінця. Внесок ціни залежить від порядку: коли вона заходить першою, це +0.2957, коли останньою — +0.4360. Саме тому один порядок нічого не вирішує. Середнє по всіх шести й є значення Шеплі: 0.364, 0.198, 0.120. І сума сходиться: 0.1402 + 0.6821 = 0.8223 — рівно прогноз моделі.
крок 1 · порядок

Беремо порядок «ціна → нового → акаунт». Ціна заходить у порожню команду: приріст 0.4359 − 0.1402 = 0.2957. Далі приходить ціна нового: 0.6547 − 0.4359 = 0.2188. Останнім — вік акаунта: 0.8223 − 0.6547 = 0.1676.

крок 2 · інший порядок

Тепер «акаунт → нового → ціна». Акаунт першим: 0.2152 − 0.1402 = 0.0750. Нового другим: 0.3863 − 0.2152 = 0.1711. Ціна останньою: 0.8223 − 0.3863 = 0.4360. Та сама ціна дістала +0.2957 у першому порядку й +0.4360 у другому — бо в другому вона одна доносить усю новину.

крок 3 · середнє

Так само для решти чотирьох порядків. Складаємо шість чисел для кожної ознаки й ділимо на шість:

ціна 0.364  ·  ціна нового 0.198  ·  вік акаунта 0.120
крок 4 · перевірка

0.364 + 0.198 + 0.120 = 0.682. Додаємо базове значення 0.140 — отримуємо 0.822, тобто рівно прогноз моделі. У зошиті ця рівність сходиться без жодного округлення, і саме тому там стоїть assert. Це властивість адитивності: сума внесків плюс база завжди дорівнює прогнозу, і саме вона робить SHAP придатним для розмови з людиною — нічого не загубилось і нічого не взялось нізвідки.

Від чого залежить відповідь

Два налаштування SHAP міняють числа, і про них зазвичай мовчать. Перше — фон. Базове значення 0.1402 — це середній прогноз саме по нашій сотні оголошень; візьми фон лише з дорогих флагманів — і база поїде, а разом із нею всі внески. Пояснення завжди відповідає на питання «чому порівняно з цим фоном», і фон варто називати вголос. Друге — склад ознак. Для того самого оголошення внесок ціни в моделі на трьох ознаках дорівнює 0.364, а в моделі на шести — 0.320: частину заслуги забрали рік і памʼять. Це не суперечність, а прямий наслідок означення: ділиться той виграш, який є, і між тими гравцями, які грають.

Кількість команд росте як 2k: три ознаки — вісім, шість — шістдесят чотири, двадцять — мільйон. Тому бібліотеки рахують значення Шеплі наближено (вибіркою порядків) або точно, але лише для дерев, де є хитрий алгоритм. У практиці ми рахуємо перебором для трьох і для шести ознак — і бачимо, що всередині бібліотеки немає магії, лише перебір.

методщо даєщо припускаєде бреше
MDIглобальний рейтинг ознак, безкоштовнощо всі ознаки мають порівнянну кількість значеньзавищує ознаки з великою кардинальністю; рахує на навчальних даних
перестановковападіння якості на нових данихщо ознаки не дублюють одна однуділить важливість між близнюками; кожна виглядає слабшою
PDPсередню форму залежності від однієї ознакищо ознаку можна крутити незалежно від рештиусереднює по комбінаціях, яких не буває; ховає різні ICE
ICEту саму форму для кожного рядка окремоте самена тисячах рядків перетворюється на кашу
LIMEлокальну лінійну модель довкола точкищо поблизу точки модель майже лінійнанестабільна: два запуски — два пояснення
SHAPчесний поділ прогнозу між ознакамищо фон підібрано правильно; що ознаки незалежнідорогий; відповідь залежить від фону й від складу ознак
прозора модельсаме правило, без посередниківнічогоможе просто гірше передбачати

08 / МежаЧого пояснення не дає

Тепер найважливіший розділ теми, і він неприємний.

SHAP, LIME, PDP, важливість — усе це моделі пояснення. Кожна з них будує спрощений опис того, як поводиться навчена модель. Опис може бути точним (у SHAP він навіть має доведені властивості) — але це опис моделі, а не світу.

Ланцюжок такий: світ → дані → модель → пояснення. Пояснення стоїть у кінці й бачить лише свого сусіда зліва. Якщо модель вивчила з даних дурницю, пояснення чесно й переконливо покаже цю дурницю — і виглядатиме так само солідно, як пояснення розумної моделі.

Звідси головне речення теми: кореляція не стає причиною від того, що її гарно намалювали. У темі 01 це вже було сказано про моделі загалом; для пояснень воно вірне подвійно, бо пояснення виглядає як відповідь на питання «чому», хоча відповідає на питання «як влаштована модель».

Наш вік акаунта — готовий приклад. Він справді причина: ми самі так згенерували дані. А перестановкова важливість дає йому 0.0143, майже нуль, бо ціна вже все розповіла. Уяви, що ти не знав генератора й читаєш цей рейтинг: висновок «вік акаунта не важливий, можна не збирати» буде абсолютно неправильним рішенням про світ, прийнятим на підставі абсолютно правильного числа про модель.

І дзеркальний випадок: якщо в дані просочиться колонка, що корелює з відповіддю випадково, SHAP чесно припише їй великий внесок. Він не має жодного способу відрізнити причину від збігу — у нього немає доступу ні до чого, крім моделі.

09 / РизикКоли пояснення небезпечне

Здавалося б, погане пояснення просто марне. Насправді гірше: воно шкідливе.

Правдоподібне пояснення вимикає перевірку. Людина, яка побачила осмислену історію — «модель позначила оголошення через занизьку ціну й свіжий акаунт» — отримує відчуття розуміння й перестає ставити питання. Історія ж сходиться. А те, що модель насправді підхопила артефакт розмітки, лишається невидимим, бо ніхто вже не шукає.

Це не філософія, а спостережений ефект: інтерфейс із поясненнями підвищує довіру до моделі незалежно від того, чи модель права. Люди частіше погоджуються з помилковою підказкою, якщо вона супроводжується поясненням. Пояснення працює як знак якості, хоча жодної якості не гарантує.

Особливо небезпечне місце. Пояснення найчастіше вимагають там, де рішення зачіпає людину — кредит, страховка, модерація, блокування продавця. Це рівно та зона, про яку йшлося в темі 38. Гарне пояснення там не замінює перевірки рівних шансів на помилку по групах: модель може мати бездоганні пояснення для кожного окремого рішення й систематично помилятись на цілій групі.

10 / ПрактикаЩо робити насправді

Порада коротка й нудна, як усе, що працює.

Почни з простої моделі як базової лінії. Логістична регресія або дерево глибини три дадуть тобі число, з яким можна порівнювати, і правило, яке можна прочитати вголос. Якщо складна модель виграє мало — бери просту, її пояснення безкоштовне й точне. Наша дошка — приклад протилежний: ліс виграє 0.24 AUC, тому складність виправдана; але дізнались ми це виміром, а не переконаннями.

Дивись на помилки очима. Візьми двадцять рядків, де модель помилилась найсильніше, і прочитай їх як людина. Це дає більше, ніж будь-який рейтинг: у половині випадків там видно витік, дублікат або зіпсовану розмітку.

Перевіряй пояснення експериментом. Це головне. Пояснення каже, що рішення тримається на памʼяті? Зміни в цьому рядку памʼять — і подивись, чи прогноз поїхав. Каже, що ознака неважлива? Прибери її й перенавчи. Пояснення — це гіпотеза, і його треба перевіряти так само, як будь-яку гіпотезу, а не приймати на віру через красиву картинку.

Не змішуй масштаби. Питання про модель — глобальні інструменти. Питання про рішення — локальні. Відповідь на одне не є відповіддю на друге.

11 / ПідсумокКуди далі

Це остання тема курсу, тож коротко про те, що ти тепер умієш.

Ти вмієш підготувати дані й не підглянути в тест; вибрати метрику під задачу, а не за звичкою; навчити сім сімейств моделей і чесно їх порівняти; побачити перенавчання по двох кривих; зібрати ансамбль; знайти структуру без міток; побудувати нейромережу з нуля разом із градієнтом; помітити несправедливість моделі й порахувати її ціну; і — від цієї теми — пояснити рішення й не повірити власному поясненню більше, ніж воно заслуговує.

Далі корисно рухатись у три різні боки: у глибину — сучасні архітектури й моделі, що працюють із текстом і зображеннями; убік інженерії — як модель живе в реальній системі, як її оновлюють і як ловлять зсув даних; убік причинності — саме той бік, якого не закриває інтерпретованість: методи, що відрізняють кореляцію від причини й дозволяють казати «якщо змінити X, зміниться Y». Останній напрям після цієї теми має бути найспокусливішим, і це правильно.

Далі в практиці. У practice.ipynb ти порахуєш feature_importances_ власноруч із дерев і звіриш зі sklearn; проженеш дослід із випадковим кодом і побачиш його третє місце; зламаєш перестановкову важливість колонкою-близнюком; порахуєш значення Шеплі перебором усіх перестановок і перевіриш адитивність assert-ом; побудуєш PDP руками й звіриш із partial_dependence; а наостанок перевіриш пояснення експериментом — змінивши одну ознаку в одному оголошенні.

Далі в темі

Теорію прочитано. Тепер закріпи її на практиці.