Skip to main content

flydnet

Use the real Drosophila connectome (FlyWire v783) as PyTorch layers. Pick any set of neurons by annotation (mushroom body, visual system, whole brain), and run them as a batched GPU spiking (LIF) or graded network whose wiring is the fly's actual synapse map. Layers are differentiable (surrogate gradients), so synapse strengths and per-cell-type parameters can be trained with backprop. Docs are in Korean.

Alpha (0.1). API가 바뀔 수 있다. 연구용 도구로 쓰고, 정확도 향상을 기대하지는 말 것 — 아래 "지금까지의 결론" 참고.

초파리 커넥톰(FlyWire v783)의 실제 배선을 신경망 층으로 쓰는 PyTorch 라이브러리. 텐서를 입력 뉴런의 발화율로 바꿔 넣고, 실제 배선을 따라 전파한 뒤 출력 뉴런의 활동을 특징으로 읽는다.

텐서 ─RateEncoder─▶ PN 발화율 ─ConnectomeLayer(실제 배선, LIF)─▶ KC/MBON 발화율 ─리드아웃─▶ 예측
  • 회로 고르기: 주석(세포 유형·계열)으로 뉴런을 골라 그 사이 연결만 남김 — 버섯체, 시각계, 전체 뇌(13.9만 뉴런)
  • 뉴런 모델: 스파이킹 LIF(Shiu et al. 2024 매개변수) 또는 연속값(graded) 뉴런
  • 역전파 학습: 연결별 또는 연결 종류별 세기, 세포 유형별 bias·막 시간 상수. 전체 뇌도 12GB GPU에서 학습
  • 대조군: 연결 수·차수를 유지한 무작위 배선, 시야 위치를 유지한 국소 무작위 배선
  • 도파민 연합 학습 리드아웃: 역전파 없이 한 번 보고 학습, 연속 학습에 강함
  • 데이터 도우미: python -m flydnet download로 FlyWire·DoOR 데이터를 받음 (패키지에는 데이터 없음)

빠른 시작 (설치와 데이터 받기 후):

import torch, flydnet as fd
mb = fd.Circuit.from_flywire()                                   # 오른쪽 버섯체: PN 344, KC 2597, APL 1, MBON 48
layer = fd.ConnectomeLayer(mb, "PN", "KC", t_ms=50, gains={"PN>KC": 2.0}, input_mode="regular")
kc = layer(torch.rand(8, 344) * 100)                             # 입력 PN 발화율(Hz) → (8, 2597) KC 발화율

연구 기록 전체(실험 10개, 방법, 한계)는 소스 저장소의 REPORT.md에 있다.

설치

# GPU(CUDA)로 쓰려면 PyTorch를 먼저 설치 (https://pytorch.org 에서 자기 CUDA 버전 명령 확인). 예:
pip install torch --index-url https://download.pytorch.org/whl/cu128
pip install flydnet

python -m flydnet download        # 데이터 받기: FlyWire v783 연결·주석 + DoOR 냄새 데이터 (약 130MB)
python -m flydnet                 # 데이터 상태 확인

pip install flydnet만 하면 PyTorch는 CPU 버전이 설치된다. 데이터는 패키지에 들어 있지 않고 download()가 ~/.flydnet/data에 받는다(위치는 fd.set_data_dir(...) 또는 환경변수 FLYDNET_FLYWIRE로 바꿀 수 있음).

개발용 (소스에서)

전용 가상환경 D:\flydnet\.venv (Python 3.11, torch 2.14.1+cu132)에 편집 모드로 설치되어 있다.

# 새로 만들 때
python -m venv .venv
.venv\Scripts\pip install torch==2.14.1 torchvision==0.29.1 --index-url https://download.pytorch.org/whl/cu132
.venv\Scripts\pip install -e ".[examples,dev]"   # 편집 모드: 코드를 고치면 바로 반영

.venv\Scripts\python -m pytest -q                # 테스트

examples/의 스크립트는 설치하지 않아도 src/를 직접 불러와서 실행된다.

사용

import flydnet as fd
mb = fd.Circuit.from_flywire()                    # 오른쪽 버섯체: PN 344, KC 2597, APL 1, MBON 48
enc = fd.RateEncoder(784, len(mb.groups["PN"]))   # 픽셀 → PN 발화율 (고정 무작위 희소 투영)
layer = fd.ConnectomeLayer(mb, "PN", "KC", t_ms=100, gains={"PN>KC": 2.0}, input_mode="regular")
feats = fd.extract(layer, enc, images)            # (n, 2597) KC 발화율
fd.train_linear(feats, y, feats_test, y_test)     # 로지스틱 회귀
mb.shuffled(seed=0)                               # 무작위 배선 대조군 (연결 수·차수는 그대로)

PyTorch 층처럼 역전파로 학습

trainable=을 주면 배선은 고정하고 연결별 세기를 nn.Parameter로 학습한다. 스파이크는 대리 기울기 (순전파는 진짜 스파이크, 역전파는 빠른 시그모이드 기울기)로, 입력 스파이크는 straight-through로 미분되어 앞쪽 층까지 기울기가 흐른다. 부호(흥분/억제)는 바뀌지 않는다.

import torch, torch.nn as nn
import flydnet as fd

mb = fd.Circuit.from_flywire()
layer = fd.ConnectomeLayer(mb, "PN", "KC", t_ms=50, dt=0.5, gains={"PN>KC": 2.0},
                           input_mode="regular", trainable=True)      # 또는 trainable=["KC>MBON"]
model = nn.Sequential(fd.RateEncoder(784, 344), layer, nn.Linear(layer.n_out, 10)).cuda()

opt = torch.optim.Adam([
    {"params": [layer.log_scale], "lr": 3e-2},                        # 커넥톰 연결 세기 (log 배율)
    {"params": model[-1].parameters(), "lr": 3e-3},
])
loss = nn.functional.cross_entropy(model(x), y)
opt.zero_grad(); loss.backward(); opt.step()

layer.weights()          # 현재 연결별 세기 (mV, 부호 포함), 순서는 layer.w_idx (post, pre)

버섯체 회로에서 배치 64, 50 ms(100스텝) 순전파 + 역전파가 RTX 5070 기준 약 0.2초, GPU 메모리 약 0.5GB.

데이터

fd.download()                         # 없는 파일만 받음: FlyWire 약 135 MB + DoOR 0.5 MB  (python -m flydnet download)
fd.set_data_dir(flywire=r"D:\my\fw")  # 이미 받아 둔 폴더를 쓰려면 (~/.flydnet/config.json에 저장)
fd.data_status()                      # 어디서 무엇을 찾았는지

위치를 찾는 순서: 함수에 준 경로 → 환경변수 FLYDNET_FLYWIRE / FLYDNET_DOOR (FLYDNET_DATA도 인정) → ~/.flydnet/config.json → ~/.flydnet/data/<flywire|door>. 받은 파일은 크기로 온전한지 확인한다. 출처: FlyWire v783 연결 파일(Shiu et al. 2024, MIT), 세포 주석(Schlegel et al. 2024), DoOR 2.0(CC BY-SA 4.0).

저장 / 불러오기

layer.save("mb.pt")                           # 배선 + 설정 + 학습한 연결 세기, 파일 하나 (버섯체 약 10 MB)
layer = fd.ConnectomeLayer.load("mb.pt")      # FlyWire 데이터 없이도 다시 만들어짐
torch.save(model.state_dict(), "m.pt")        # 표준 PyTorch 방식도 그대로
readout.save("r.pt"); fd.AssocReadout.load("r.pt")

모두 torch.load(weights_only=True)로 읽힌다 (텐서·기본 자료형만 저장).

메모리 절약: 그래디언트 체크포인팅

layer = fd.ConnectomeLayer(..., trainable=True, checkpoint_every=20)   # 20스텝 구간마다 다시 계산

역전파 때 구간의 중간 상태를 다시 계산해 시간 방향 메모리를 줄인다. 출력은 완전히 같고, 기울기는 GPU 합산 순서 오차(상대 1e-7) 안에서 같다. 버섯체, 100 ms(200스텝): 배치 64에서 0.74 → 0.18 GB, 배치 256에서 1.97 → 0.51 GB, 계산은 1.3~2배.

큰 회로 학습: 연결 단위 역전파

torch.sparse.mm의 역전파는 연결 기울기를 뉴런 수 × 뉴런 수 크기로 만든다 (전체 뇌면 77 GB). SparsePropagate는 필요한 연결 칸만 계산한다 (torch.sparse.sampled_addmm, 메모리 = 연결 수). 연결 세기도 순전파당 한 번만 계산한다. torch.sparse.mm과 같은 기울기를 내는 것을 gradcheck로 확인했다.

others = ["optic", "central", "visual_projection", "ascending", "descending",
          "sensory_ascending", "visual_centrifugal", "motor", "endocrine"]
brain = fd.Circuit.from_flywire({"SENS": ("super_class", "sensory"), "REST": ("super_class", others)}, side=None)
layer = fd.ConnectomeLayer(brain, "SENS", "REST", t_ms=50, dt=0.5, input_mode="regular",
                           trainable=True, checkpoint_every=10)

학습 1스텝 (순전파 + 역전파 + Adam, 50 ms = 100스텝, RTX 5070):

회로 연결 배치 GPU 메모리 시간
감각 → 중심 뇌, 4.9만 뉴런 486만 8 0.88 GB (이전 13.9 GB) 0.8초 (이전 18초)
전체 뇌, 13.9만 뉴런 1,509만 8 2.16 GB (이전 77 GB 요구) 2.2초
전체 뇌 1,509만 32 3.65 GB 2.6초

큰 회로를 시험할 때는 torch.cuda.set_per_process_memory_fraction(0.75)처럼 상한을 걸어 두면, GPU 메모리가 넘칠 때 Windows 가상 메모리(C 드라이브)로 흘러가지 않고 바로 오류가 난다.

구성

모듈 내용
circuit.py Circuit: 주석으로 뉴런 그룹을 골라 그 사이 연결만 남긴 회로, shuffled() 대조군, summary()
encoders.py RateEncoder: 텐서 → 입력 뉴런 발화율
layers.py ConnectomeLayer: 배치 LIF 시뮬레이션 (희소행렬 곱, GPU). input_mode="regular"/"poisson"
readout.py extract(): 데이터 → 발화율 특징, train_linear(): 리드아웃 학습

실험 ① 버섯체 저장소(reservoir) — MNIST

python examples/mnist_reservoir.py (train 60k / test 10k, RTX 5070에서 회로 하나당 약 2.5분)

설정: 100 ms, PN→KC 배율 2.0 (KC 약 7% 활성 = 실제 초파리 수준), 규칙적 입력 스파이크

특징 차원 test
픽셀 (기준선) 784 92.72%
PN 발화율 (인코더 출력) 344 92.41%
KC — 실제 배선 2597 89.56%
KC — 무작위 배선 ×3 2597 89.05 / 89.20 / 89.06%
MBON — 실제 배선 48 24.39%
MBON — 무작위 배선 ×3 48 26.97 / 35.10 / 32.02%

해석

  • KC 층은 픽셀보다 약 3%p 낮다. 고정된 스파이킹 층을 거치면서 정보가 줄어든다.
  • 실제 배선이 무작위 배선보다 KC에서 0.4~0.5%p 높다. 무작위 대조군 3개 모두보다 높지만 차이가 작아 (테스트 1만 개의 표준오차 약 0.3%p) 확정적이지 않다.
  • MBON 48개는 거의 쓸모없고 실제 배선이 오히려 낮다. MBON은 원래 학습된 KC→MBON 시냅스로 읽어야 하는 출력이라, 학습 없는 배선만으로는 의미 있는 판독이 안 되는 것으로 보인다 → 실험 ②의 동기.

개발 중 발견한 점

  • 포아송(무작위) 입력, 100 ms에서는 같은 이미지를 다시 넣어도 켜지는 KC 집합이 26%만 겹쳐 리드아웃이 잡음을 외웠다 (test 54%). 규칙적 입력으로 바꿔 같은 입력 → 같은 반응이 되게 했다.

실험 ② 도파민 학습 리드아웃 (역전파 없음) — MNIST

python examples/mnist_dopamine.py (실험 ①의 KC 특징 캐시 사용, 1분 이내)

숫자마다 MBON 같은 출력 뉴런 하나. KC→출력 시냅스를 KC 활동 × 도파민 국소 규칙으로 학습 (fd.DopamineReadout).

  • bidir: 틀렸을 때 정답 출력 강화 + 이긴 오답 출력 약화 (양방향 도파민 가소성)
  • assoc: 정답 출력만 그 클래스 평균 패턴으로 강화 + 출력별 시냅스 총량 정규화. 학습 순서와 무관
  • ltd, ltd_err, ltp: 단방향 규칙. 전체 학습에서 37~73%로 약해 실험에서 제외

A. 전체 학습 (test, bidir은 3회 평균)

특징 역전파(로지스틱) 도파민 bidir 도파민 assoc
픽셀 92.72% 88.99 ± 0.63% 82.16%
KC 실제 배선 89.46% 84.77 ± 0.50% 74.09%
KC 무작위 배선 89.06% 84.39 ± 2.64% 74.61%

B. 연속 학습 (0/1 → 2/3 → 4/5 → 6/7 → 8/9 순서로 한 번씩, 지금까지 본 숫자 전체 정확도)

특징 방법 단계별 마지막 후 첫 과제(0/1)
KC 실제 배선 역전파 99.8 → 70.3 → 58.8 → 44.9 → 40.5 5.8%
KC 실제 배선 도파민 bidir 99.8 → 47.4 → 30.4 → 24.8 → 19.1 0.0%
KC 실제 배선 도파민 assoc 99.3 → 90.2 → 82.8 → 79.6 → 74.1 89.4%
픽셀 도파민 assoc 99.8 → 92.9 → 87.8 → 86.8 → 82.2 93.0%

해석

  • 오류를 고치는 규칙(역전파, bidir)은 전체 학습에서 강하지만 연속 학습에서 앞 과제를 거의 다 잊는다. bidir은 새 숫자를 배울 때 틀린 답이 대부분 옛 숫자라 옛 출력을 계속 약화시켜 역전파보다 더 잊는다.
  • assoc는 다른 출력을 건드리지 않아 망각이 원리상 없다 → 연속 학습 최종 74% (역전파 40%). 대신 전체 학습 성능은 낮다 (74% vs 89%).
  • 실제 배선 효과는 여기서도 없다: KC 실제 ≈ 무작위, 그리고 assoc는 KC보다 픽셀에서 더 좋다 (82% vs 74%). 이점은 버섯체 배선이 아니라 학습 규칙(순서 무관 연합 학습)에서 나온다.

실험 ③ 합성 냄새 과제 — 실제 배선 대 무작위 배선

python examples/odor_task.py (약 8분)

  • 냄새 = 사구체 56개 활성 벡터. fd.GlomerularEncoder가 같은 사구체의 단일 사구체형 PN(139개)에 같은 발화율을 넣음 (실제 더듬이엽 구조)
  • 클래스 = 냄새 원형 여러 개의 묶음 (fd.synthetic_odors(protos_per_class=K)) → 사구체 공간에서 선형 분리가 어려움. 차원을 넓히는 KC 층이 이론적으로 유리한 과제 (Babadi & Sompolinsky 2014)
  • PN→KC 배율 3.0 (KC 약 5% 활성), 로지스틱 회귀 리드아웃, 과제 seed 5개 × 무작위 배선 5개
설정 (클래스 × 원형, 잡음, 학습/클래스) 사구체 KC 실제 KC 무작위 (5개 범위) 실제 − 무작위 실제 승률
20 × 5, 0.5, 20 68.6 76.7 75.1 (73.9~76.0) +1.64 ± 1.36 84%
20 × 10, 0.5, 20 41.5 52.4 50.5 (49.7~51.3) +1.89 ± 0.68 100%
10 × 20, 0.5, 40 38.0 54.2 53.8 (52.5~54.9) +0.46 ± 1.73 48%
50 × 5, 0.8, 20 53.5 47.7 46.0 (45.5~46.3) +1.73 ± 0.65 96%

전체 20개(설정 × seed) 중 18개에서 실제 배선이 무작위 평균보다 높음, 평균 +1.43%p. (같은 설정을 다시 돌리면 GPU 연산 순서 때문에 0.5%p 안팎으로 흔들린다)

정정: 처음 결과(+1.82%p, 19/20)는 무작위화 결함이 있던 버전이다. 섞을 때 같은 PN→KC 연결이 두 번 생기면 합쳐져서 무작위 회로의 KC가 실제보다 입력을 약 5% 적게 받았다 (PN→KC 중복 497개, KC→KC 1,557개). shuffled()가 이제 중복·자기 연결을 맞바꿈으로 없앤다. 실험 ①의 무작위 대조군 숫자는 결함 버전 기준이다.

해석

  • 실제 배선 효과는 결함을 고친 뒤에도 유지된다 (+1.43%p, 18/20). 원형 20개 설정에서는 차이가 없다.
  • KC 활성 비율로는 설명되지 않는다: 실제 5.33%, 무작위 5.12~6.00%.
  • 냄새가 무작위 합성이라 '자연 냄새 통계에 맞춰진 배선'이라는 설명은 아니다.
  • 비선형 과제에서 KC 층은 사구체 값보다 8~16%p 낫다 (이론대로). 단, 잡음이 크고 클래스가 많으면 (마지막 행) 오히려 사구체 값이 낫다.

실험 ③-b 이점은 어느 연결에서 나오나

python examples/odor_ablation.py (약 11분). 한 종류의 연결만 섞고 나머지는 실제 그대로 둠. 손실 = 실제 − 해당 조건 (3설정 × 5seed, 조건마다 무작위 3개)

섞은 연결 손실 (%p) 양수
전체 +1.61 ± 0.48 12/15
PN→KC +0.66 ± 0.45 10/15
나머지 (MBON 관련, PN끼리 등) +0.32 ± 0.28 11/15
KC→KC −0.48 ± 0.35 4/15
APL ↔ KC −0.50 ± 0.31 5/15

(± = 표준오차)

해석

  • 전체를 섞으면 확실히 손해지만, 한 종류만 섞어서는 뚜렷한 손실이 없다. 이점이 한 연결에 있지 않고 여러 연결 구조가 함께 만드는 것으로 보인다 (손실이 더해지지 않음).
  • KC→KC, APL은 섞으면 오히려 약간 좋아진다 → 이점의 출처가 아님.
  • 결함 버전에서는 PN→KC 손실이 +1.17 (13/15)로 유일한 출처처럼 보였는데, 그중 상당 부분이 중복 연결로 무작위 KC 입력이 줄어든 탓이었다.
  • 실제 PN→KC 배선은 무작위보다 같은 사구체 입력을 중복해 받는 KC가 많고 (14.7% 대 10.2%), KC 아형(γ, αβ, α'β')마다 받는 사구체 분포의 치우침이 크다. 이것이 성능에 기여하는지는 확인 못 함.

실험 ④ 실제 냄새 데이터 (DoOR 2.0)

python examples/door_task.py (약 5분)

데이터: DoOR.data의 door_response_matrix.csv, door_mappings.csv, odor.csv를 data/door/에 둔다 (Münch & Galizia 2016, Sci Rep 6:21841, CC BY-SA 4.0. 저장소에는 포함하지 않음). fd.door_odors()가 수용체 반응을 FlyWire 사구체 이름에 맞춰 사구체 벡터로 바꾼다 (자발 발화 빼고 0 아래는 0). 사구체 56개 중 47개가 측정됨. 사구체 20개 이상 측정된 냄새 154개 사용, 냄새당 켜진 사구체 평균 9.6개.

과제 사구체 KC 실제 KC 무작위 (5개 범위) 실제 − 무작위
A. 냄새 구별 (154개, seed 3개) 59.4 52.2 52.4 (52.0~53.1) −0.21 ± 0.20, 1/3
B. 새 냄새의 화학 계열 (8계열 116개, 5겹 × seed 3개, 찍기 22%) 55.1 51.5 51.5 (50.9~51.8) −0.06 ± 0.56, 7/15

해석

  • 실제 냄새에서는 실제 배선의 이점이 없다. 두 과제 모두 실제 ≈ 무작위.
  • 실제 냄새에서는 KC 층이 사구체 값보다도 낮다 (3.6~7.2%p). 합성 과제에서 KC가 유리했던 건 클래스를 일부러 '원형 여러 개의 묶음'(선형 분리 어려움)으로 만들었기 때문이고, 실제 냄새 구별·계열 분류는 사구체 공간에서 이미 꽤 선형적인 것으로 보인다.
  • 합성 과제의 작은 우위(+1.43%p)는 실제 냄새 통계로 옮겨지지 않았다. 그 우위는 실제 배선이 자연 냄새에 맞춰져 있어서가 아니라, 합성 과제의 특정 구조와 맞물린 결과일 가능성이 크다.
  • 한계: 사구체 9개는 측정이 없어 0으로 들어가고, 측정된 칸도 86%가 빈 표에서 채운 값이다. PN→KC 배율(3.0)은 합성 과제 기준으로 맞춘 값이다.

실험 ⑤ 실제 냄새 혼합물 — 조건부 구별 (XOR형)

python examples/door_mixtures.py (약 6분)

DoOR 냄새 4개 (A, B, C, D)마다 AB+, CD+, AC−, BD− 를 학습 (Young et al. 2011의 형태 학습 과제). 모든 냄새가 보상·무보상에 한 번씩 들어가서, 반응이 더해지는 한 선형 분류기로는 풀 수 없다. 혼합물 = 포화(A + B), PN→KC 배율 3.0 (실제 냄새에서 KC 6% 활성), 묶음마다 따로 로지스틱 회귀. (A+, B+, AB− 부정 패턴은 '켜진 사구체가 많으면 무보상'이라는 선형 규칙으로 풀려서 쓰지 않았다.)

특징 정확도 (찍기 50%, 묶음 200개)
사구체 61.6%
KC 실제 배선 80.5%
KC 무작위 배선 ×5 81.3% (80.5~81.8)

실제 − 무작위 = −0.77 ± 0.33%p (표준오차), 실제가 나은 묶음 96 / 못한 묶음 101.

해석

  • KC 층은 확실히 필요하다: 선형으로 못 푸는 과제에서 사구체 61.6% → KC 80.5% (+19%p).
  • 하지만 실제 배선일 필요는 없다: 무작위 배선이 오히려 약간 낫다 (차이는 작음).
  • 배선 가설에 대한 결론: 이점은 '확장 + 희소화'라는 일반 구조에서 나오고, FlyWire의 구체적인 PN→KC 배선이 추가로 주는 이점은 이 시험들에서 보이지 않는다 (MNIST, 합성 냄새 +1.4%p, 실제 냄새 구별·계열·혼합물 모두 차이 없음).

실험 ⑥ 연속 학습 — 도파민 연합 학습 대 역전파(재생 버퍼)

python examples/continual_mnist.py (약 1분, KC 특징은 실험 ① 캐시)

fd.AssocReadout(n_in, n_classes, per_class=k): 클래스마다 출력(원형) k개. 샘플이 오면 그 클래스 출력 중 가장 잘 맞는 하나에만 보상 도파민 → 그 출력이 받은 샘플들의 평균 패턴이 됨 (빈 출력부터 채움). 다른 클래스의 시냅스는 절대 바뀌지 않는다. k=1이면 실험 ②의 assoc와 같다.

전체 학습 (한 번 보기, 역전파 없음): k를 늘리면 픽셀 82.2 → 95.4% (k=50), KC 74.1 → 89.9%. 픽셀 k=50은 역전파 로지스틱 회귀(92.7%)보다 높다.

class-incremental split MNIST (0/1 → … → 8/9, 과제당 1에폭, 3회 평균, 최종 = 숫자 10개 전체)

특징 방법 (저장량: 클래스당 벡터 수) 최종 마지막 후 0/1
픽셀 역전파 (0) 37.8% 2.1%
픽셀 역전파 + 재생 20 74.8% 76.7%
픽셀 역전파 + 재생 50 82.2% 86.4%
픽셀 연합 20 93.9% 98.8%
픽셀 연합 50 95.2% 99.2%
KC 역전파 + 재생 50 75.6% 84.1%
KC 연합 50 89.6% 98.2%

해석

  • 같은 저장량에서 연합 학습이 재생 버퍼 역전파보다 13~14%p 높고, 첫 과제를 거의 그대로 기억한다.
  • 정직하게: 이 규칙은 머신러닝의 '클래스별 온라인 k-평균 원형 분류기'와 같고, 원형(평균) 기반 분류가 연속 학습에 강하다는 것은 알려져 있다 (예: iCaRL의 nearest-mean-of-exemplars). 비교한 역전파도 로지스틱 회귀 1에폭이라 약한 편이다. 새로운 건 이것을 버섯체 도파민 회로의 국소 규칙으로 해석·구현한 점이다.
  • 여기서도 KC 특징이 픽셀보다 못하다 (89.6% 대 95.2%).
  • (수정 기록: 처음 버전은 한 묶음의 같은 클래스 샘플이 모두 첫 빈 원형으로 들어가는 버그가 있었다. 고친 뒤 MNIST 숫자는 ±0.5%p 안에서만 바뀌었다.)

실험 ⑦ 연합 학습에 KC 층이 필요한가 — 실제 냄새 조건부 구별

python examples/door_assoc.py (약 6분). 실험 ⑤와 같은 XOR형 과제, 냄새 4개 묶음 200개, 묶음마다 따로 학습.

리드아웃 사구체 KC 실제 KC 무작위
로지스틱 (역전파, 선형) 61.4 81.1 81.4
MLP (역전파, 은닉 64) 98.5 93.1 93.5
연합 k=1 58.4 62.8 64.1
연합 k=2 99.2 98.3 98.5
연합 k=4 98.8 98.1 98.2
연합 k=10 98.3 97.4 97.7

(찍기 50%, 표준오차 0.3~0.8%p)

해석

  • 원형이 2개 이상인 연합 학습은 KC 없이 사구체 값만으로 XOR형 과제를 푼다 (99.2%). 원형 여러 개 = 그 자체로 비선형 분류기라서. KC를 거치면 오히려 약간 낮다 (98.3%).
  • KC 층이 필요한 건 리드아웃이 선형일 때뿐이다 (로지스틱 61 → 81%). 역전파 MLP도 사구체에서 가장 좋다.

실험 ⑧ 커넥톰 층을 역전파로 학습 — MNIST

python examples/train_backprop.py (약 11분). 일반 PyTorch 학습 루프, 학습 2만 / 평가 1만, 3에폭, dt 0.5 ms, 50 ms 창, 선형 층 학습률 3e-3, 커넥톰 연결 세기 학습률 3e-2.

설정 학습 파라미터 에폭별 test
커넥톰 고정 + 선형 25,980 80.4 → 83.1 → 84.4
연결 19만 개 전부 학습 (KC 판독) 216,836 84.2 → 85.1 → 85.9
KC→MBON만 학습 (MBON 48개 판독) 24,064 67.3 → 72.5 → 74.4
무작위 배선, 전부 학습 (KC 판독) 216,836 84.8 → 84.2 → 85.9

해석

  • 커넥톰 층을 통과하는 역전파가 동작한다. 연결 세기가 실제로 바뀌고(배율 595% 범위 0.24.3배), 고정 층보다 1.5%p 좋아졌다.
  • MBON 48개 판독은 고정이면 24%(실험 ①)였지만 **KC→MBON 연결만 학습하면 74%**가 된다. 초파리에서 학습이 일어나는 바로 그 자리만 바꿔도 판독이 쓸모 있어진다.
  • 학습해도 실제 배선과 무작위 배선은 같다 (85.87% 대 85.91%).
  • 설정이 실험 ①과 달라(dt 0.5, 50 ms, Adam 3에폭) 고정 층 숫자도 다르다 (84.4% 대 89.6%).

실험 ⑨ 시각계 — 전체 시야 운동 방향

python examples/visual_motion.py --circuit real (약 8분, --circuit shuffled). 오른쪽 시각계 전체 (fd.visual_circuit(), 뉴런 4.8만, 연결 429만, 세포 유형 584개), 연속값 뉴런(neuron="graded"), 연결 종류별 배율과 유형별 bias·시간 상수를 학습. 격자 8방향을 T4a–d·T5a–d 아형별 평균 8개로 맞힘.

정확도 (찍기 12.5%) 뉴런별 방향 선택 지수 (중앙값)
실제 배선 100% 0.003–0.014
무작위 배선 100% 0.000–0.002

둘 다 100%지만 뉴런은 방향을 거의 가리지 않음(실제 T4/T5는 0.3–0.8). 수천 개 평균의 미세한 차이를 리드아웃이 증폭한 지름길 → 실험 ⑩으로.

실험 ⑩ 시각계 — 국소 운동 방향

python examples/visual_local_motion.py --circuit real|shuffled|local --seed 0 (약 6분). 운동 감지 경로만 (MOTION_PATHWAY, 뉴런 2.4만, 연결 43만). 시야를 칸 13개로 나눠 칸마다 다른 방향, 리드아웃은 모든 칸이 공유. 1500스텝, seed 3개씩.

회로 위치 대응 방향 구조 정확도 (찍기 12.5%)
실제 배선 있음 있음 24.0 ± 8.3% (2/3 학습)
국소 무작위 (shuffled(local=..., merge=...)) 있음 없음 31.6 ± 2.8% (3/3)
전체 무작위 (shuffled()) 없음 없음 12.5 ± 0.2% (0/3)

해석

  • 전체 무작위가 못 배운 이유는 시야 위치 대응이 깨져서(입력이 1.5 대신 14기둥 밖에서 옴, 기울기 약 100배 작음).
  • 위치 대응만 남기고 아형별 방향 구조를 지운 회로도 똑같이 배운다 → 이 과제에서 세부 배선의 이점은 없음.
vc = fd.visual_circuit().subset(fd.MOTION_PATHWAY)     # 광수용체 → 라미나 → 메둘라 → T4/T5
xy = fd.column_map(fd.visual_circuit())                 # 뉴런별 시야 좌표 (기둥 간격 단위)
layer = fd.ConnectomeLayer(vc.normalized(), fd.PHOTORECEPTORS, ["T4a", "T4b", "T4c", "T4d"],
                           neuron="graded", params={"w_syn": 3.0}, bias=0.2, t_ms=300, dt=2.0,
                           trainable=True, share="pair", train_neurons=True)

지금까지의 결론

질문 답
실제 FlyWire 배선이 무작위 배선보다 나은가 아니다. 합성 냄새에서만 +1.4%p, MNIST·실제 냄새 4가지 과제에서 차이 없음
시각계처럼 배선이 기능을 정하는 회로에서는 시야 위치 대응은 필수(없으면 학습 불가). 하지만 그 안의 세부 배선(아형별 방향 구조)은 학습으로 대체됨
KC 층(확장 + 희소화)이 도움이 되나 리드아웃이 선형일 때만 (XOR형 과제 +19%p). 원형 연합 학습·MLP에는 불필요하거나 손해
도파민 연합 학습(AssocReadout)이 쓸모 있나 그렇다. 한 번 보기, 역전파 없음, 연속 학습에서 같은 저장량 재생 버퍼보다 13~14%p 높음. 단 알려진 원형 분류기와 같은 원리

다음 단계 아이디어

  • 질문을 "커넥톰이 정확도를 높이는가"(10개 실험에서 아니다)에서 **"커넥톰이 어떤 해법을 찾게 하는가"**로 바꾸기: 학습된 T4/T5 아형별 선호 방향이 배선 속 방향 구조(Mi9→Mi4 어긋남)와 일치하는지, 국소 무작위에서는 제멋대로인지
  • 적은 학습량에서의 차이 (타고난 방향 구조가 출발점으로 유리한지)
  • 실제 측정과 비교 (T4/T5 선호 방향·시간 반응, Lappalainen et al. 2024 방식)

Metadata

Release files for flydnet 0.1.0

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for flydnet 0.1.0
File Size Uploaded
flydnet-0.1.0.tar.gz 60.5 kB Details

Built distribution (wheel)

Table of built distributions (wheels) for flydnet 0.1.0
File Interpreter ABI Platform
flydnet-0.1.0-py3-none-any.whl Python 3 none any Details

Total release size: 105.7 kB

Release files / flydnet-0.1.0.tar.gz

Download URL flydnet-0.1.0.tar.gz
Size 60.5 kB
Tags Source
SHA-256 checksum
How to use checksums
84dc6b836f844a959789e36372f85242bfb6954fd2c2fac4f8eda211df23df2b
BLAKE2b-256 checksum
How to use checksums
849da17242c4d30a83917258ae04dcfe48791f4c596843088d25bd46d1b0ca4a
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release files / flydnet-0.1.0-py3-none-any.whl

Download URL flydnet-0.1.0-py3-none-any.whl
Size 45.2 kB
Tags Python 3
SHA-256 checksum
How to use checksums
81807421401fe0ab6e952aec2a742abcfa73e2e267b6801f69aacc12e5a77523
BLAKE2b-256 checksum
How to use checksums
1c594a414223ee2632f4b0dc12fde30cc00b268853bf52fb2243c83823e0099d
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
No
Uploaded via twine/7.0.0 CPython/3.11.9

Release history Release notifications | RSS feed

This release

0.1.0 This release

2 release files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page