Dědičnost (Heredity)
Nejnovější verze Pythonu, kterou byste v tomto kurzu měli používat, je Python 3.12.
Napište AI pro posouzení pravděpodobnosti, že člověk bude mít určitý genetický znak.
$ python heredity.py data/family0.csv
Harry:
Gene:
2: 0.0092
1: 0.4557
0: 0.5351
Trait:
True: 0.2665
False: 0.7335
James:
Gene:
2: 0.1976
1: 0.5106
0: 0.2918
Trait:
True: 1.0000
False: 0.0000
Lily:
Gene:
2: 0.0036
1: 0.0136
0: 0.9827
Trait:
True: 0.0000
False: 1.0000
Kdy úkol dokončit
Jak získat pomoc
Výchozí informace
Mutované varianty genu GJB2 patří mezi hlavní příčiny poruch sluchu u novorozenců. Každý člověk nese dvě varianty genu, a může tedy mít 0, 1 nebo 2 kopie varianty GJB2 spojené s poruchou sluchu. Bez genetického testování však není snadné vědět, kolik kopií mutovaného GJB2 člověk má. Jde o „skrytý stav“: informaci s pozorovatelným účinkem, například poruchou sluchu, kterou však nemusíme přímo znát. Někteří lidé totiž mohou mít 1 nebo 2 kopie mutovaného GJB2, a přesto poruchu sluchu neprojevovat, zatímco jiní mohou mít poruchu sluchu i bez kopií mutovaného GJB2.
Každé dítě dědí jednu kopii genu GJB2 od každého rodiče. Pokud má rodič dvě kopie mutovaného genu, předá dítěti mutovaný gen. Pokud nemá žádnou kopii mutovaného genu, mutovaný gen dítěti nepředá. Pokud má jednu kopii, předá ji s pravděpodobností 0,5. Po předání genu však existuje určitá pravděpodobnost další mutace: varianty způsobující poruchu sluchu na variantu, která ji nezpůsobuje, nebo naopak.
Všechny tyto vztahy můžeme zkusit modelovat vytvořením bayesovské sítě všech relevantních proměnných, například jako níže pro rodinu se dvěma rodiči a jedním dítětem.

Každý člen rodiny má náhodnou proměnnou Gene představující počet kopií určitého genu, například varianty GJB2 spojené s poruchou sluchu: hodnotu 0, 1 nebo 2. Každý má také náhodnou proměnnou Trait, která je yes nebo no podle toho, zda se u něj znak založený na genu, například porucha sluchu, projevuje. Od Gene každého člověka vede šipka k jeho Trait, což vyjadřuje, že geny ovlivňují pravděpodobnost určitého znaku. Od proměnných Gene matky i otce navíc vedou šipky k Gene dítěte: geny dítěte závisí na genech rodičů.
Vaším úkolem je pomocí modelu odvozovat závěry o populaci. Na základě informací o lidech, jejich rodičích a tom, zda mají určitý pozorovatelný znak, například ztrátu sluchu, způsobovaný daným genem, AI odvodí pravděpodobnostní rozdělení genů každého člověka a také pravděpodobnostní rozdělení toho, zda se u jednotlivých lidí daný znak projeví.
Začínáme
- Stáhněte výchozí kód z https://cdn.cs50.net/ai/2023/x/projects/2/heredity.zip a rozbalte jej.
Porozumění výchozímu kódu
Prohlédněte si jednu z ukázkových datových sad v adresáři data: otevřete data/family0.csv v textovém editoru nebo tabulkovém programu, například Google Sheets, Excelu či Apple Numbers. První řádek definuje sloupce CSV: name, mother, father a trait. Další řádek uvádí, že Harryho matkou je Lily a otcem James; prázdné pole trait znamená, že nevíme, zda Harry daný znak má. James nemá v datové sadě uvedené rodiče, což označují prázdná pole mother a father, a znak se u něj projevuje, jak uvádí 1 v poli trait. Lily také nemá v datové sadě uvedené rodiče, ale znak se u ní neprojevuje, jak uvádí 0 v poli trait.
Otevřete heredity.py a nejprve se podívejte na definici PROBS. PROBS je slovník obsahující konstanty pravděpodobností různých událostí. Všechny souvisejí s počtem kopií určitého genu, který dále označujeme jednoduše jako „gen“, a s tím, zda se u člověka na jeho základě projevuje určitý „znak“. Data jsou volně založena na pravděpodobnostech varianty genu GJB2 spojené s poruchou sluchu a samotné poruchy sluchu. Změnou hodnot byste ale mohli AI použít i pro odvozování závěrů o jiných genech a znacích!
Nejprve PROBS["gene"] představuje nepodmíněné pravděpodobnostní rozdělení genu, tedy pravděpodobnost, pokud o rodičích člověka nic nevíme. Podle dat ve výchozím kódu má člověk v populaci 1% pravděpodobnost dvou kopií genu, 3% pravděpodobnost jedné kopie a 96% pravděpodobnost žádné kopie.
Dále PROBS["trait"] představuje podmíněnou pravděpodobnost projevu znaku, například poruchy sluchu. Ve skutečnosti jde o tři rozdělení, jedno pro každou možnou hodnotu gene. PROBS["trait"][2] tedy udává rozdělení znaku při dvou kopiích genu: v tomto případě je pravděpodobnost projevu 65 % a neprojevení 35 %. Pokud má člověk 0 kopií genu, má 1% pravděpodobnost projevu a 99% pravděpodobnost neprojevení znaku.
Nakonec PROBS["mutation"] udává pravděpodobnost, že gen zmutuje z cílového genu na necílový nebo naopak. Pokud má například matka dvě kopie cílového genu, jednu dítěti předá, ale existuje 1% pravděpodobnost, že zmutuje na necílový gen. Naopak pokud matka žádnou kopii nemá a cílový gen nepředá, existuje 1% pravděpodobnost, že předaná kopie zmutuje na cílový gen. I když tedy ani jeden rodič nemá žádnou kopii cílového genu, jejich dítě může mít 1 nebo dokonce 2 kopie.
Vypočítané pravděpodobnosti budou nakonec založeny na těchto hodnotách v PROBS.
Nyní se podívejte na funkci main. Nejprve načte data ze souboru do slovníku people. people mapuje jméno každého člověka na další slovník s informacemi o něm: jménem, matkou a otcem, pokud jsou v datech uvedeni, a údajem o tom, zda u něj byl daný znak pozorován (True, pokud ano, False, pokud ne, a None, pokud to nevíme).
Poté main definuje slovník probabilities, kde jsou všechny pravděpodobnosti zpočátku 0. Právě to projekt nakonec vypočítá: pro každého člověka AI určí rozdělení počtu kopií genu i toho, zda má daný znak. Například probabilities["Harry"]["gene"][1] bude pravděpodobnost, že Harry má 1 kopii genu, a probabilities["Lily"]["trait"][False] pravděpodobnost, že se u Lily znak neprojevuje.
Pokud tento zápis neznáte, slovník probabilities se vytváří pomocí slovníkové komprehenze (dictionary comprehension) v Pythonu. Ta zde vytvoří jednu dvojici klíč–hodnota pro každého person ve slovníku people.
Tyto pravděpodobnosti chceme vypočítat na základě evidence: víme, že se u některých lidí znak projevuje, nebo neprojevuje, a chceme stanovit uvedené pravděpodobnosti. Z přednášky si připomeňte, že podmíněnou pravděpodobnost lze vypočítat sečtením všech sdružených pravděpodobností splňujících evidenci a následnou normalizací tak, aby každé rozdělení mělo součet 1. Vaším úkolem je implementovat tři funkce: joint_probability pro výpočet sdružené pravděpodobnosti, update pro její přidání do existujícího rozdělení a normalize, aby součet každého rozdělení nakonec byl 1.
Specifikace
Dokončete implementace joint_probability, update a normalize.
Funkce joint_probability má přijmout slovník lidí spolu s informacemi o tom, kdo má kolik kopií genu a u koho se projevuje znak. Má vrátit sdruženou pravděpodobnost všech těchto událostí.
- Funkce přijímá čtyři vstupní hodnoty:
people,one_gene,two_genesahave_trait.peopleje slovník lidí popsaný v části Porozumění výchozímu kódu. Klíče představují jména a hodnoty jsou slovníky obsahující klíčemotherafather. Můžete předpokládat, že buď jsoumotherafatheroba prázdné, tedy rodiče nejsou v datové sadě uvedeni, nebomotherafatheroba odkazují na jiné lidi ve slovníkupeople.one_geneje množina všech lidí, pro které chceme vypočítat pravděpodobnost jedné kopie genu.two_genesje množina všech lidí, pro které chceme vypočítat pravděpodobnost dvou kopií genu.have_traitje množina všech lidí, pro které chceme vypočítat pravděpodobnost přítomnosti znaku.- Pro člověka, který není v
one_geneanitwo_genes, chceme vypočítat pravděpodobnost žádných kopií genu. Pro člověka, který není vhave_trait, chceme vypočítat pravděpodobnost, že znak nemá.
- Pokud například rodinu tvoří Harry, James a Lily, volání této funkce s
one_gene = {"Harry"},two_genes = {"James"}atrait = {"Harry", "James"}má vypočítat pravděpodobnost, že Lily má nula kopií genu, Harry jednu kopii, James dvě kopie, u Harryho a Jamese se znak projevuje a u Lily nikoli. - U lidí bez uvedených rodičů použijte k určení pravděpodobnosti daného počtu kopií genu rozdělení
PROBS["gene"]. - U lidí s rodiči v datové sadě každý rodič dítěti náhodně předá jednu ze svých dvou kopií. Existuje pravděpodobnost
PROBS["mutation"], že zmutuje z cílového genu na necílový nebo naopak. - Rozdělení
PROBS["trait"]použijte k výpočtu pravděpodobnosti, že člověk daný znak má, nebo nemá.
Funkce update přidává novou sdruženou pravděpodobnost do existujících rozdělení v probabilities.
- Funkce přijímá pět vstupních hodnot:
probabilities,one_gene,two_genes,have_traitap.probabilitiesje slovník lidí popsaný v části Porozumění výchozímu kódu. Každý člověk je mapován na rozdělení"gene"a"trait".one_geneje množina lidí s jednou kopií genu v současném sdruženém rozdělení.two_genesje množina lidí se dvěma kopiemi genu v současném sdruženém rozdělení.have_traitje množina lidí se znakem v současném sdruženém rozdělení.pje pravděpodobnost sdruženého rozdělení.
- Pro každého
personvprobabilitiesmá funkce aktualizovat rozděleníprobabilities[person]["gene"]aprobabilities[person]["trait"]přidánímpk odpovídající hodnotě každého rozdělení. Všechny ostatní hodnoty mají zůstat nezměněné. - Pokud je například
"Harry"současně vtwo_genesihave_trait, přičte sepdoprobabilities["Harry"]["gene"][2]aprobabilities["Harry"]["trait"][True]. - Funkce nemá vracet žádnou hodnotu: má pouze aktualizovat slovník
probabilities.
Funkce normalize aktualizuje slovník pravděpodobností tak, aby každé rozdělení bylo normalizované, tedy mělo součet 1 při zachování vzájemných poměrů.
- Funkce přijímá jedinou hodnotu:
probabilities.probabilitiesje slovník lidí popsaný v části Porozumění výchozímu kódu. Každý člověk je mapován na rozdělení"gene"a"trait".
- Obě rozdělení každého člověka v
probabilitiesmá funkce normalizovat tak, aby součet jejich hodnot byl 1 a relativní poměry zůstaly stejné. - Pokud je například
probabilities["Harry"]["trait"][True]rovno0.1aprobabilities["Harry"]["trait"][False]rovno0.3, funkce má první hodnotu změnit na0.25a druhou na0.75: součet je nyní 1 a druhá hodnota je stále trojnásobkem první. - Funkce nemá vracet žádnou hodnotu: má pouze aktualizovat slovník
probabilities.
V heredity.py neměňte nic kromě tří funkcí určených k implementaci ve specifikaci. Můžete však napsat další funkce a případně importovat další moduly standardní knihovny Pythonu. Pokud je znáte, můžete importovat také numpy nebo pandas, ale žádné další moduly Pythonu od třetích stran používat nemáte.
Příklad sdružené pravděpodobnosti
Abychom vám pomohli promyslet výpočet sdružených pravděpodobností, uvádíme níže příklad.
Uvažujte následující hodnotu people:
{
'Harry': {'name': 'Harry', 'mother': 'Lily', 'father': 'James', 'trait': None},
'James': {'name': 'James', 'mother': None, 'father': None, 'trait': True},
'Lily': {'name': 'Lily', 'mother': None, 'father': None, 'trait': False}
}
Ukážeme výpočet joint_probability(people, {"Harry"}, {"James"}, {"James"}). Podle argumentů je one_gene rovno {"Harry"}, two_genes rovno {"James"} a have_trait rovno {"James"}. Jde tedy o pravděpodobnost, že Lily má 0 kopií genu a nemá znak, Harry má 1 kopii a nemá znak a James má 2 kopie a má znak.
Začneme Lily. Na pořadí lidí nezáleží, pokud násobíme správné hodnoty, protože násobení je komutativní. Lily má 0 kopií genu s pravděpodobností 0.96, tedy PROBS["gene"][0]. Pokud má 0 kopií, nemá znak s pravděpodobností 0.99, tedy PROBS["trait"][0][False]. Pravděpodobnost, že má 0 kopií genu a nemá znak, je tedy 0.96 * 0.99 = 0.9504.
Dále uvažujeme Jamese. James má 2 kopie genu s pravděpodobností 0.01, tedy PROBS["gene"][2]. Při dvou kopiích je pravděpodobnost projevu znaku 0.65. Pravděpodobnost, že má 2 kopie a má znak, je tedy 0.01 * 0.65 = 0.0065.
Nakonec uvažujeme Harryho. Jaká je pravděpodobnost, že má 1 kopii genu? To může nastat dvěma způsoby: buď gen získá od matky a nikoli od otce, nebo od otce a nikoli od matky. Jeho matka Lily má 0 kopií genu, takže Harry od ní získá gen s pravděpodobností 0.01, tedy PROBS["mutation"], protože jedinou možností je mutace; s pravděpodobností 0.99 od matky gen nezíská. Jeho otec James má 2 kopie genu, takže Harry od něj získá gen s pravděpodobností 0.99, tedy 1 - PROBS["mutation"], ale od své matky získá gen s pravděpodobností 0.01, pravděpodobností mutace. Oba případy lze sečíst: 0.99 * 0.99 + 0.01 * 0.01 = 0.9802, což je pravděpodobnost jedné kopie genu u Harryho. Poznámka překladatele: poslední věta o přenosu od matky věrně zachovává slovo „mother“ v originále. V kontextu popisu otce je tato formulace v původním textu matoucí.
Pokud má Harry 1 kopii genu, pravděpodobnost, že nemá znak, je 0.44, tedy PROBS["trait"][1][False]. Pravděpodobnost, že má 1 kopii genu a nemá znak, je tedy 0.9802 * 0.44 = 0.431288.
Celá sdružená pravděpodobnost je proto součinem uvedených hodnot všech tří lidí: 0.9504 * 0.0065 * 0.431288 = 0.0026643247488.
Nápověda
- Připomeňte si, že sdruženou pravděpodobnost více událostí lze získat vynásobením jejich pravděpodobností. U každého dítěte ale pravděpodobnost určitého počtu genů závisí na genech rodičů.
Testování
Pokud chcete, můžete po nastavení check50 ve svém systému spustit následující příkaz pro ověření správnosti kódu. Není to povinné; můžete úkol jednoduše odevzdat podle kroků na konci specifikace a stejné testy se spustí na našem serveru. V každém případě kód také sami zkompilujte a otestujte!
check50 ai50/projects/2024/x/heredity
Následujícím příkazem zkontrolujte styl svého kódu pomocí style50.
style50 heredity.py
Pamatujte, že kromě modulů standardní knihovny Pythonu nesmíte importovat žádné jiné moduly než ty, které jsou zde výslovně povoleny. Jinak se nejen nespustí check50, ale ani submit50 nebude moci úkol ohodnotit, protože používá check50. Pokud k tomu dojde, pravděpodobně jste importovali něco nepovoleného nebo výchozí kód jinak změnili způsobem, který zadání nepovoluje. Existují nástroje, které některé projekty značně zjednodušují, ale to zde není cílem: učíte se na nižší úrovni. Pokud zde není řečeno, že je můžete používat, používat je nesmíte.
Jak úkol odevzdat
- Navštivte tento odkaz, přihlaste se účtem GitHub a klikněte na Authorize cs50. Poté zaškrtněte políčko udělující týmu kurzu přístup k vašim odevzdaným pracím a klikněte na Join course.
- Nainstalujte Git a volitelně nainstalujte
submit50. - Pokud máte nainstalovaný
submit50, spusťtesubmit50 ai50/projects/2024/x/heredityJinak pomocí Gitu odešlete práci do
https://github.com/me50/USERNAME.git, kdeUSERNAMEje vaše uživatelské jméno GitHub, do větve pojmenovanéai50/projects/2024/x/heredity.
Pokud kód odevzdáváte přímo pomocí Gitu místo submit50, nezahrnujte jiné soubory ani složky než ty, které máte podle výše uvedené specifikace skutečně upravit. Jinými slovy nenahrávejte celý svůj adresář!
Práce by měla být ohodnocena do pěti minut. Pak můžete na https://cs50.me/cs50ai zobrazit aktuální postup!