Xlera8

Wprowadzenie do modeli dyfuzji dla uczenia maszynowego

Ten artykuł został pierwotnie opublikowany w MontażAI i ponownie opublikowane w TOPBOTS za zgodą autora.

Modele dyfuzyjne to modele generatywne, które w ciągu ostatnich kilku lat zyskały znaczną popularność i nie bez powodu. Garść przełomowych artykułów wydanych w latach 2020 sam pokazali światu, do czego zdolne są modele dyfuzyjne, takie jak pokonanie GAN[6] na syntezie obrazu. Ostatnio praktycy mogli zobaczyć modele dyfuzyjne stosowane w: DALL-E2, model generowania obrazu OpenAI wydany w zeszłym miesiącu.

Różne obrazy generowane przez DALL-E 2 (źródło).

Biorąc pod uwagę ostatnią falę sukcesu modeli dyfuzyjnych, wielu praktyków uczenia maszynowego z pewnością jest zainteresowanych ich wewnętrznym działaniem. W tym artykule przyjrzymy się teoretyczne podstawy modeli dyfuzyjnych, a następnie zademonstruj, jak generować obrazy za pomocą Model dyfuzji w PyTorch. Zanurzmy się!

Jeśli te szczegółowe treści edukacyjne są dla Ciebie przydatne, zapisz się na naszą listę mailingową AI zostać powiadomionym, gdy wydamy nowy materiał. 

Modele dyfuzyjne – wprowadzenie

Modele dyfuzyjne są generatywny modele, co oznacza, że ​​są używane do generowania danych podobnych do danych, na których są szkolone. Zasadniczo modele dyfuzji działają przez: niszczenie danych treningowych poprzez kolejne dodawanie szumu Gaussa, a następnie nauka powrotu do zdrowia dane przez cofania ten hałaśliwy proces. Po przeszkoleniu możemy użyć modelu dyfuzji do generowania danych po prostu przekazywanie losowo próbkowanego szumu przez wyuczony proces odszumiania.

Modele dyfuzyjne mogą być używane do generowania obrazów z szumu (zaadaptowane z źródło)

Dokładniej, model dyfuzji to model zmiennej latentnej, który mapuje do przestrzeni latentnej za pomocą stałego łańcucha Markowa. Ten łańcuch stopniowo dodaje szum do danych w celu uzyskania przybliżonego tylnego q(x1: T|x0), gdzie x1,…, XT są zmiennymi latentnymi o tej samej wymiarowości co x0. Na poniższym rysunku widzimy taki łańcuch Markowa zamanifestowany dla danych obrazu.

Ostatecznie obraz jest asymptotycznie przekształcany w czysty szum Gaussa. The cel uczenia modelu dyfuzji jest poznanie rewers proces – czyli trening pθ(xt-1|xt). Cofając się wzdłuż tego łańcucha, możemy wygenerować nowe dane.

Zalety modeli dyfuzyjnych

Jak wspomniano powyżej, badania nad modelami dyfuzyjnymi eksplodowały w ostatnich latach. Zainspirowany nierównowagową termodynamiką[1], Modele dyfuzyjne obecnie produkują Najnowocześniejsza jakość obrazu, których przykłady można zobaczyć poniżej:

Oprócz najwyższej jakości obrazu, modele dyfuzyjne oferują wiele innych korzyści, w tym: nie wymagający przeszkolenia kontradyktoryjnego. Trudności w szkoleniu przeciwników są dobrze udokumentowane; a w przypadkach, gdy istnieją niekonkurencyjne alternatywy o porównywalnej wydajności i efektywności treningu, zwykle najlepiej jest je wykorzystać. Jeśli chodzi o efektywność treningu, modele dyfuzyjne mają również dodatkowe zalety: skalowalność i równoległość.

Chociaż wydaje się, że modele dyfuzyjne dają wyniki niemal znikąd, istnieje wiele ostrożnych i interesujących wyborów matematycznych i szczegółów, które stanowią podstawę tych wyników, a najlepsze praktyki wciąż ewoluują w literaturze. Przyjrzyjmy się teraz bardziej szczegółowo teorii matematycznej leżącej u podstaw modeli dyfuzji.

Modele dyfuzyjne – głębokie nurkowanie

Jak wspomniano powyżej, model dyfuzji składa się z proces przekazywania (lub proces dyfuzji), w którym dana (zwykle obraz) jest progresywnie zaszumiona, i a proces odwrotny (lub proces odwróconej dyfuzji), w którym szum jest przekształcany z powrotem w próbkę z rozkładu docelowego.

Przejścia łańcucha próbkowania w procesie do przodu można ustawić na warunkowe gaussy, gdy poziom szumu jest wystarczająco niski. Połączenie tego faktu z założeniem Markowa prowadzi do prostej parametryzacji procesu forward:

Uwaga matematyczna

Mówiliśmy o uszkodzeniu danych przez dodanie Hałas gaussowski, ale na początku może być niejasne, gdzie wykonujemy ten dodatek. Zgodnie z powyższym równaniem, na każdym etapie łańcucha po prostu pobieramy próbki z rozkładu Gaussa, którego średnia jest poprzednią wartością (tj. obrazem) w łańcuchu.

Te dwa stwierdzenia są równoważne. To znaczy

Aby zrozumieć dlaczego, użyjemy lekkiego nadużycia notacji, twierdząc

Gdzie ostateczna implikacja wynika z matematycznej równoważności między sumą zmiennych losowych a splotem ich rozkładów – zob. ta strona Wikipedii po więcej informacji.

Innymi słowy, wykazaliśmy, że stwierdzenie rozkładu kroku czasowego uwarunkowanego poprzednim za pomocą średniej rozkładu Gaussa jest równoważne stwierdzeniu, że rozkład danego kroku czasowego jest rozkładem poprzedniego z dodatkiem szumu Gaussa. Pominęliśmy skalary wprowadzone przez harmonogram wariancji i pokazaliśmy to dla jednego wymiaru dla uproszczenia, ale podobny dowód dotyczy wielowymiarowych Gaussów.

Gdzie β1,…,βT jest harmonogramem wariancji (wyuczonym lub ustalonym), który, jeśli jest dobrze zachowany, zapewnia to xT jest prawie izotropowym Gaussem dla wystarczająco dużego T.

Biorąc pod uwagę założenie Markowa, łączny rozkład zmiennych latentnych jest iloczynem gaussowskich warunkowych przejść łańcuchowych (zmodyfikowanych z źródło).

Jak wspomniano wcześniej, „magia” modeli dyfuzyjnych tkwi w: proces odwrotny. Podczas uczenia model uczy się odwracać ten proces dyfuzji w celu wygenerowania nowych danych. Zaczynając od czystego szumu Gaussa p(xT):=N(xT,0,I) model uczy się wspólnego rozkładu pθ(x0: T) jak

gdzie poznawane są zależne od czasu parametry przejść Gaussa. Zauważ w szczególności, że sformułowanie Markowa zakłada, że ​​dany rozkład przejścia odwróconej dyfuzji zależy tylko od poprzedniego kroku czasowego (lub kolejnego kroku czasowego, w zależności od tego, jak na to spojrzysz):

Trening

Model dyfuzji jest szkolony przez znalezienie odwróconych przejść Markowa, które maksymalizują prawdopodobieństwo danych uczących. W praktyce uczenie równoważnie polega na minimalizowaniu górnej granicy wariacyjnej na prawdopodobieństwie logarytmu ujemnego.

Szczegóły notacji

Zauważ, że LVLB jest technicznie i górny związany (negatyw ELBO), który staramy się minimalizować, ale nazywamy go LVLB dla spójności z literaturą.

Staramy się przepisać LVLB pod względem Rozbieżności Kullbacka-Leiblera (KL). Rozbieżność KL jest asymetryczną statystyczną miarą odległości określającą, ile wynosi jeden rozkład prawdopodobieństwa P różni się od rozkładu referencyjnego Q. Jesteśmy zainteresowani sformułowaniem LVLB pod względem rozbieżności KL, ponieważ rozkłady przejścia w naszym łańcuchu Markowa są gaussami, a rozbieżność KL między Gaussami ma formę zamkniętą.

Co to jest dywergencja KL?

Matematyczna postać dywergencji KL dla rozkładów ciągłych to

Podwójne słupki wskazują, że funkcja nie jest symetryczna względem swoich argumentów.

Poniżej widać dywergencję KL o różnym rozkładzie P (niebieski) z rozkładu referencyjnego Q (czerwony). Zielona krzywa wskazuje funkcję wewnątrz całki w definicji rozbieżności KL powyżej, a całkowita powierzchnia pod krzywą przedstawia wartość rozbieżności KL P od Q w dowolnym momencie wartość, która jest również wyświetlana numerycznie.

Casting Porno lvlb w zakresie rozbieżności KL

Jak wspomniano wcześniej, możliwe jest [1] przepisać LVLB prawie całkowicie pod względem rozbieżności KL:

gdzie

Szczegóły wyprowadzenia

Granica wariacyjna jest równa

Zastępując rozkłady ich definicjami, biorąc pod uwagę nasze założenie Markowa, otrzymujemy

Używamy reguł logarytmicznych, aby przekształcić wyrażenie w sumę logów, a następnie wyciągamy pierwszy wyraz

Korzystając z twierdzenia Bayesa i naszego założenia Markowa, wyrażenie to staje się

Następnie dzielimy środkowy termin za pomocą reguł logów

Izolując drugi termin, widzimy

Wstawiając to z powrotem do naszego równania na LVLB, mamy

Używając reguł logów, zmieniamy kolejność

d8

Następnie zwracamy uwagę na następującą równoważność rozbieżności KL dla dowolnych dwóch rozkładów:

Wreszcie, stosując tę ​​równoważność do poprzedniego wyrażenia, dochodzimy do:

Kondycjonowanie postępu procesu do przodu na x0 w Lt-1 wyniki w przystępnej formie, która prowadzi do wszystkie rozbieżności KL są porównaniami między Gaussami. Oznacza to, że rozbieżności można dokładnie obliczyć za pomocą wyrażeń w formie zamkniętej, a nie szacunków Monte Carlo[3].

Wybór modelu

Po ustaleniu matematycznych podstaw naszej funkcji celu musimy teraz dokonać kilku wyborów dotyczących sposobu implementacji naszego modelu dyfuzji. W przypadku procesu forward jedynym wymaganym wyborem jest zdefiniowanie harmonogramu wariancji, którego wartości zazwyczaj rosną w trakcie procesu forward.

W przypadku procesu odwrotnego zdecydowanie wybieramy parametryzację/architektury modelu rozkładu Gaussa. Zanotuj wysoki stopień elastyczności na co pozwalają modele dyfuzji – tylko Wymaganiem naszej architektury jest to, aby dane wejściowe i wyjściowe miały tę samą wymiarowość.

Poniżej bardziej szczegółowo omówimy szczegóły tych wyborów.

Proces przekazywania i LT

Jak wspomniano powyżej, w odniesieniu do procesu forward, musimy zdefiniować harmonogram wariancji. W szczególności ustawiliśmy je na stałe zależne od czasu, ignorując fakt, że można się ich nauczyć. Na przykład[3], rozkład liniowy z β1= 10-4 do βTMożna użyć =0.2 lub być może serii geometrycznej.

Niezależnie od wybranych poszczególnych wartości, fakt, że schemat wariancji jest stały powoduje, że LT stając się stałym w odniesieniu do naszego zestawu parametrów, które można nauczyć, co pozwala nam ignorować go, jeśli chodzi o trening.

Proces odwrotny i L1:T-1

Teraz omówimy wybory wymagane przy definiowaniu procesu odwrotnego. Przypomnijmy, że odwrotne przejścia Markowa zdefiniowaliśmy jako gaussowskie:

Musimy teraz zdefiniować formy funkcyjne μθ lub Σθ. Chociaż istnieją bardziej skomplikowane sposoby parametryzacji Σθ[5], po prostu ustawiamy

Oznacza to, że zakładamy, że wielowymiarowy gauss jest iloczynem niezależnych gaussów o identycznej wariancji, wartości wariancji, która może się zmieniać w czasie. My ustawić te wariancje, aby były równoważne z naszym harmonogramem wariancji procesu w przód.

Biorąc pod uwagę to nowe sformułowanie Σθ, mamy

co pozwala nam się transformować

do

gdzie pierwszy wyraz różnicy jest kombinacją liniową xt i x0 to zależy od rozkładu wariancji βt. Dokładna forma tej funkcji nie jest istotna dla naszych celów, ale można ją znaleźć w [3].

Znaczenie powyższej proporcji jest takie, że najprostsza parametryzacja μθ po prostu przewiduje średnią tylną dyfuzji. Co ważne, autorzy [3] faktycznie stwierdził, że trening μθ przewidzieć hałas składnik na dowolnym etapie daje lepsze wyniki. W szczególności niech

gdzie

Prowadzi to do następującej alternatywnej funkcji straty, którego autorzy [3] prowadzi do bardziej stabilnego treningu i lepszych wyników:

Autorzy [3] zwracają również uwagę na powiązania tego sformułowania modeli dyfuzji z modelami generatywnymi dopasowującymi wyniki, opartymi na dynamice Langevina. Rzeczywiście, wydaje się, że modele dyfuzji i modele oparte na wynikach mogą być dwiema stronami tego samego medalu, podobnie jak niezależny i równoległy rozwój mechaniki kwantowej opartej na falach i mechaniki kwantowej opartej na macierzach, ujawniając dwa równoważne sformułowania tych samych zjawisk[2].

Architektura sieci

Podczas gdy nasza uproszczona funkcja straty ma na celu wytrenowanie modelu ϵθ, wciąż nie zdefiniowaliśmy architektury tego modelu. Zauważ, że tylko wymogiem dla modelu jest to, aby jego wymiarowość wejściowa i wyjściowa były identyczne.

Biorąc pod uwagę to ograniczenie, być może nie dziwi fakt, że modele dyfuzji obrazu są powszechnie implementowane z architekturami podobnymi do U-Net.

Dekoder procesu odwróconego i L0

Ścieżka w procesie odwrotnym składa się z wielu przekształceń w ciągłych warunkowych rozkładach Gaussa. Pod koniec procesu odwrotnego przypomnij sobie, że staramy się wyprodukować obraz, który składa się z całkowitych wartości pikseli. Dlatego musimy wymyślić sposób na uzyskanie dyskretne (log) prawdopodobieństwa dla każdej możliwej wartości piksela we wszystkich pikselach.

Można to zrobić, ustawiając ostatnie przejście w łańcuchu odwróconej dyfuzji na an niezależny dekoder dyskretny. Aby określić prawdopodobieństwo danego obrazu x0 podane x1, najpierw narzucamy niezależność między wymiarami danych:

gdzie D jest wymiarowością danych i indeksem górnym i wskazuje wyodrębnienie jednej współrzędnej. Celem jest teraz określenie, jak prawdopodobne jest, że każda wartość całkowita jest dla danego piksela dany rozkład w możliwych wartościach dla odpowiedniego piksela w lekko zaszumionym obrazie w czasie t=1:

gdzie rozkłady pikseli dla t=1 pochodzą z poniższej wielowymiarowej gaussowskiej, której macierz diagonalnej kowariancji pozwala nam podzielić rozkład na iloczyn jednowymiarowych gaussowskich, po jednym dla każdego wymiaru danych:

Przyjmujemy, że obrazy składają się z liczb całkowitych w 0,1,…255 (jak robią to standardowe obrazy RGB), które zostały przeskalowane liniowo do [−1,1]. Następnie rozbijamy rzeczywistą linię na małe „kubeczki”, gdzie dla danej przeskalowanej wartości piksela x, przedział dla tego zakresu to [x−1/255, x+1/255]. Prawdopodobieństwo wartości piksela x, biorąc pod uwagę jednowymiarowy rozkład Gaussa odpowiedniego piksela w x1, jest obszar pod tym jednowymiarowym rozkładem Gaussa w wiadrze wyśrodkowanym na x.

Poniżej możesz zobaczyć obszar dla każdego z tych segmentów z ich prawdopodobieństwem dla średniej 0 Gaussa, co w tym kontekście odpowiada rozkładowi o średniej wartości piksela 255/2 (połowa jasności). Czerwona krzywa przedstawia rozkład określonego piksela w t = 1 obraz, a obszary podają prawdopodobieństwo odpowiedniej wartości piksela w t = 0 obraz.

Uwaga techniczna

Pierwszy i ostatni zasobnik rozciąga się na -inf i +inf, aby zachować całkowite prawdopodobieństwo.

Dawać t = 0 wartość piksela dla każdego piksela, wartość pθ(x0|x1) jest po prostu ich produktem. Proces ten zwięźle opisuje następujące równanie:

gdzie

i

Biorąc pod uwagę to równanie dla pθ(x0|x1), możemy obliczyć końcowy wyraz LVLB co nie jest sformułowane jako rozbieżność KL:

Cel końcowy

Jak wspomniano w ostatniej sekcji, autorzy [3] stwierdzili, że przewidywanie składowej szumu obrazu w danym kroku czasowym daje najlepsze wyniki. Ostatecznie wykorzystują następujący cel:

Algorytmy uczenia i próbkowania dla naszego modelu dyfuzji można zatem zwięźle przedstawić na poniższym rysunku:

Podsumowanie teorii modelu dyfuzji

W tej sekcji szczegółowo zagłębiliśmy się w teorię modeli dyfuzji. Łatwo jest zagłębić się w matematyczne szczegóły, dlatego w tej sekcji poniżej zapisujemy najważniejsze punkty, aby zachować orientację z lotu ptaka:

  1. Nasz model dyfuzji jest sparametryzowany jako a Łańcuch Markowa, co oznacza, że ​​nasze zmienne latentne x1,…, XT zależą tylko od poprzedniego (lub następnego) kroku czasowego.
  2. Połączenia rozkłady przejścia w łańcuchu Markowa są Gaussian, gdzie proces przekazywania wymaga harmonogramu wariancji, a parametry procesu odwrotnego są wyuczone.
  3. Proces dyfuzji zapewnia, że ​​xT is rozłożony asymptotycznie jako izotropowy Gaussian dla wystarczająco dużego T.
  4. W naszym przypadku Naprawiono harmonogram wariancji, ale też można się tego nauczyć. W przypadku ustalonych harmonogramów śledzenie postępu geometrycznego może dać lepsze wyniki niż postęp liniowy. W obu przypadkach wariancje generalnie rosną wraz z upływem czasu w szeregu (tj. βij dla mnie
  5. Modele dyfuzyjne są bardzo elastyczny i pozwól na każdy architektura, której wymiarowość wejściowa i wyjściowa jest taka sama, jaka ma być użyta. Wiele wdrożeń używa Podobny do U-Net architektury.
  6. Połączenia cel szkolenia jest maksymalizacja prawdopodobieństwa danych treningowych. Objawia się to dostrajaniem parametrów modelu do zminimalizować górną granicę wariacyjną ujemnego logarytmicznego prawdopodobieństwa danych.
  7. Prawie wszystkie wyrazy w funkcji celu można oddać jako KL Rozbieżności w wyniku naszego założenia Markowa. Te wartości stać się zdolnym do obliczania biorąc pod uwagę, że używamy Gaussów, pomijając tym samym konieczność wykonywania przybliżenia Monte Carlo.
  8. Ostatecznie, używając uproszczony cel szkolenia wytrenowanie funkcji, która przewiduje składową szumu danej zmiennej latentnej, daje najlepsze i najbardziej stabilne wyniki.
  9. dyskretny dekoder służy do uzyskiwania logarytmicznych prawdopodobieństw dla wartości pikseli jako ostatni krok w procesie odwróconej dyfuzji.

Mając w głowie ten ogólny przegląd modeli dyfuzyjnych, przejdźmy do tego, jak używać modeli dyfuzyjnych w PyTorch.

Modele dyfuzyjne w PyTorch

Chociaż modele dyfuzji nie zostały jeszcze zdemokratyzowane w tym samym stopniu, co inne starsze architektury/podejścia w uczeniu maszynowym, nadal istnieją implementacje dostępne do użycia. Najłatwiejszym sposobem użycia modelu dyfuzyjnego w PyTorch jest użycie denoising-diffusion-pytorch pakiet, który implementuje model dyfuzji obrazu, taki jak ten omówiony w tym artykule. Aby zainstalować pakiet, po prostu wpisz w terminalu następujące polecenie:

pip install denoising_diffusion_pytorch

Minimalny przykład

Aby wytrenować model i wygenerować obrazy, najpierw importujemy niezbędne pakiety:

import torch
from denoising_diffusion_pytorch import Unet, GaussianDiffusion

Następnie definiujemy naszą architekturę sieci, w tym przypadku U-Net. The dim parametr określa liczbę map obiektów przed pierwszym próbkowaniem w dół, a dim_mults parametr podaje mnożniki dla tej wartości i kolejne próby w dół:

model = Unet(
 dim = 64,
 dim_mults = (1, 2, 4, 8)
)

Teraz, gdy mamy już zdefiniowaną architekturę sieci, musimy zdefiniować sam model dyfuzji. Przekazujemy model U-Net, który właśnie zdefiniowaliśmy, wraz z kilkoma parametrami – rozmiarem obrazów do wygenerowania, liczbą kroków czasowych w procesie dyfuzji oraz wyborem między normami L1 i L2.

diffusion = GaussianDiffusion(
 model,
 image_size = 128,
 timesteps = 1000, # number of steps
 loss_type = 'l1' # L1 or L2
)

Teraz, kiedy model dyfuzji jest zdefiniowany, nadszedł czas na trening. Generujemy losowe dane do trenowania, a następnie trenujemy model dyfuzji w zwykły sposób:

training_images = torch.randn(8, 3, 128, 128)
loss = diffusion(training_images)
loss.backward()

Po przeszkoleniu modelu możemy wreszcie wygenerować obrazy za pomocą sample() metoda diffusion obiekt. Tutaj generujemy 4 obrazy, które są tylko szumem, biorąc pod uwagę, że nasze dane treningowe były losowe:

sampled_images = diffusion.sample(batch_size = 4)

Szkolenie z danych niestandardowych

Połączenia denoising-diffusion-pytorch Pakiet pozwala również na trenowanie modelu dyfuzji na określonym zbiorze danych. Po prostu wymień 'path/to/your/images' ciąg ze ścieżką katalogu zestawu danych w Trainer() obiekt poniżej i zmień image_size do odpowiedniej wartości. Następnie po prostu uruchom kod, aby wytrenować model, a następnie spróbuj jak poprzednio. Zwróć uwagę, że PyTorch musi być skompilowany z włączoną obsługą CUDA, aby móc używać Trainer klasa:

from denoising_diffusion_pytorch import Unet, GaussianDiffusion, Trainer
model = Unet(
 dim = 64,
 dim_mults = (1, 2, 4, 8)
).cuda()
diffusion = GaussianDiffusion(
 model,
 image_size = 128,
 timesteps = 1000, # number of steps
 loss_type = 'l1' # L1 or L2
).cuda()
trainer = Trainer(
 diffusion,
 'path/to/your/images',
 train_batch_size = 32,
 train_lr = 2e-5,
 train_num_steps = 700000, # total training steps
 gradient_accumulate_every = 2, # gradient accumulation steps
 ema_decay = 0.995, # exponential moving average decay
 amp = True # turn on mixed precision
)
trainer.train()

Poniżej można zobaczyć progresywne odszumianie od wielowymiarowego szumu Gaussa do cyfr MNIST podobne do odwróconej dyfuzji:

Ostatnie słowa

Modele dyfuzji to koncepcyjnie proste i eleganckie podejście do problemu generowania danych. Ich najnowocześniejsze wyniki w połączeniu z niekonkurencyjnym treningiem wyniosły ich na wielkie wyżyny i można się spodziewać dalszych ulepszeń w nadchodzących latach, biorąc pod uwagę ich rodzący się status. W szczególności stwierdzono, że modele dyfuzyjne mają zasadnicze znaczenie dla wydajności najnowocześniejszych modeli, takich jak DALL-E2.

Referencje

[1] Głębokie uczenie nienadzorowane przy użyciu termodynamiki nierównowagi

[2] Modelowanie generatywne poprzez szacowanie gradientów dystrybucji danych

[3] Odszumiające modele probabilistyczne dyfuzji

[4] Ulepszone techniki uczenia modeli generatywnych opartych na wynikach

[5] Ulepszone modele probabilistyczne odszumiania dyfuzji

[6] Modele dyfuzji pokonują GAN podczas syntezy obrazu

[7] POSUW: W kierunku fotorealistycznego generowania i edycji obrazu za pomocą modeli dyfuzji sterowanych tekstem

[8] Hierarchiczne generowanie obrazu warunkowego tekstem za pomocą funkcji CLIP Latents

Podoba ci się ten artykuł? Zarejestruj się, aby otrzymywać więcej aktualizacji badań AI.

Damy Ci znać, gdy wydamy więcej artykułów podsumowujących takich jak ten.

Czat z nami

Cześć! Jak mogę ci pomóc?