Skip to content

Repository files navigation

공정 데이터 보정 — 2022년 연구를 4년 뒤에 다시 열어본 기록

2022년, 공정 연구원이던 시절에 정상상태 데이터 보정 문제 하나를 붙들고 있었다. 유량 16개짜리 네트워크에 편향 오차를 하나 심어놓고 엑셀 해찾기로 풀었고, 나중에 GEKKO로 옮겼다. 2026년에 그 파일을 다시 열었다.

검정의 기준선이 4년 내내 틀린 값으로 잡혀 있었다. 그런데도 예제는 정답을 냈다. 아무도 눈치채지 못한 이유가 정확히 그것이다.

이 저장소는 그때의 연구와 그걸 다시 뜯어본 기록이다.

English version: README.en.md

뭐가 틀렸었나

통계량 자체는 맞았다. 최적점에서의 Σ((x̂ᵢ−yᵢ)/σᵢ)²는 교과서에 나오는 (Ay−b)ᵀ(ARAᵀ)⁺(Ay−b)와 대수적으로 같은 값이다. 2022년 코드는 왜 그런지 설명하지 않은 채 그 값을 제대로 계산하고 있었다.

틀린 건 그 값을 비교할 기준분포였다. 자유도를 ν = n − 1 = 15로 박아뒀는데 맞는 값은 rank(A R Aᵀ) = 8이다.

잡음만 있는 데이터에서 검정통계량의 분포

잡음만 넣고 3000번 뽑았다. 통계량은 χ²₈에 얹힌다. 기준선을 15.51에 그으면 의도한 대로 5%가 기각된다. 2022년 기준선 25.00에서는 0.1%만 걸린다.

2022년 (ν = 15) 맞는 값 (ν = 8)
χ²₀.₉₅ 기준치 25.00 15.51
실제 오경보율 0.0016 0.05
탐지 가능한 최소 편향 (x6) 34.8 (7.0σ) 27.4 (5.5σ)

5.5σ에서 7σ 사이 편향은 옛 검정에 잡히지 않았다. 이 예제에서 심은 편향은 26σ고 통계량이 348까지 올라가서 두 기준선 다 넘는다. 그래서 데모가 돌아갔다. 드러나지 않았을 뿐 사라진 건 아니다. 검정이 사실상 잠들어 있었다. tests/test_diagnostics.py가 양쪽을 다 붙잡아둔다. 잡음만 있는 2000회의 오경보율, 그리고 옛 기준선이 놓쳤을 6σ 편향.

자유도는 네트워크가 결정하는 값이다. 한 줄이면 계산되고, 짐작하면 조용히 틀린다.

파이썬에서 시작된 실수가 아니었다

엑셀 원본을 열어보니 워크북이 똑같이 계산하고 있었다. D19가 변수 개수를 세고 D20이 거기서 1을 빼서 자유도라고 부른다. 셀이 아직 그대로 있다.

GEKKO 번역은 충실했다. 실수는 한 단계 앞, 엑셀에서 문제를 세울 때 나왔고 그대로 실려 왔다. 이쪽이 더 쓸모 있는 이야기다. 번역은 누가 다시 읽어보기라도 하지만 잘 돌아가는 스프레드시트에서 물려받은 가정은 아무도 건드리지 않는다.

tests/test_workbook_provenance.py가 워크북을 직접 읽어서 확인한다. 계측값, 표준편차, 제약행렬 128개 원소가 지금도 정확히 일치한다. 2022년에 해찾기를 손으로 열여섯 번 돌린 serial elimination 결과도 identify_gross_error()와 상대오차 2e-6 안에서 맞는다. 4년 간격을 둔 두 구현이 같은 답을 냈다.

이 저장소가 아닌 것

범용 보정 라이브러리가 아니다. 선형 정상상태 문제 하나만 다루고 그 밖은 건드리지 않는다. 일반적인 문제는 IDAES나 NeqSim 같은 오픈소스와 상용 생산회계 제품이 이미 감당하고 있다.

조정값은 모델이 강제한 추정치이지 참값이 아니다.

왜 이걸 붙들고 있었나

2022년의 질문은 평범했다. 플랜트 계측값이 물질수지를 안 닫는데 어느 숫자를 얼마나 믿을지 누군가는 정해야 했다. 손으로 하면 재현이 안 된다. 엔지니어 둘이 서로 다른 스트림을 건드리고, 그 선택을 따라 수율이며 손실이며 하류의 숫자가 전부 흔들린다.

엑셀로 시작한 건 데이터가 거기 있었고 최적화가 눈에 보였기 때문이다. 목적함수가 셀 하나에 있고 잔차가 열로 서 있고 반복할 때마다 조정값이 바뀐다.

문제

스트림 16개, 노드 8개. 정상상태에서 모든 노드가 수지를 맞춰야 하니 노드 하나가 행 하나가 되어 A x = b가 된다.

16개 유량 네트워크

계측값은 이 수지를 만족하지 않는다. 원래 안 한다. 보정은 계측값에서 최대한 덜 움직이면서 수지를 닫는 값을 찾는 문제다. 얼마나 덜 움직일지는 센서를 얼마나 믿느냐로 정한다.

x̂ = argmin (x − y)ᵀ R⁻¹ (x − y)   subject to   A x = b

선형 등식 제약이면 닫힌 해가 있다. solver가 필요 없다.

x̂ = y − R Aᵀ (A R Aᵀ)⁺ (A y − b)

예제가 보여주는 것

x6에 +130(26σ)짜리 편향을 심었다. 여기서 질문 세 개가 갈라진다.

단계 질문 이 예제의 답
탐지 뭔가 어긋나 있나 기각. g = 348.19, ν = 8, p ≈ 2e-70
식별 어느 계측값인가 x6, 유일하게. serial elimination
재구성 원래 얼마였어야 하나 나머지 열다섯 개로부터 120.0

답보다 중요한 결과가 두 개 나온다.

탐지 전에 보정하면 상황이 나빠진다. 편향을 그대로 둔 채 최소제곱을 돌리면 16개 스트림이 전부 움직이고 멀쩡한 열다섯 개가 틀린 하나를 흡수하느라 나빠진다. 편향은 제거되지 않고 퍼진다. 탐지가 먼저여야 하는 이유다.

싼 진단은 애먼 센서까지 잡는다. 측정검정은 x6을 1등으로 올려놓지만(|z| = 18.7) 고장 난 게 하나뿐인데 16개 중 9개에 깃발을 꽂는다. 용의자 순위일 뿐 지목이 아니다.

결과

global test statistic : 348.194281
degrees of freedom    : 8  (= rank(A R A^T))
chi2 0.95 critical    : 15.507313
p-value               : 2.199384e-70
||A x_hat - b||       : 3.296949e-13

Serial elimination. 축소된 문제마다 ν = 7이다.

   i  g_after_delete   nu       crit            p  verdict
x5          321.5594    7    14.0671    1.496e-65  still inconsistent
x6            0.0000    7    14.0671    1.000e+00  consistent -> candidate
x7          304.8426    7    14.0671    5.590e-62  still inconsistent

x6은 120.000000으로 재구성된다. 참값도 120이다.

이 숫자를 곧이곧대로 읽으면 안 된다

재구성이 1e-14까지 맞는다. 보이는 것보다 훨씬 별거 아니다.

심어놓은 편향을 빼면 이 데이터에는 잡음이 없다. 나머지 열다섯 개가 이미 수지를 정확히 만족하고 있어서, 틀린 하나를 빼고 나면 추정할 게 남지 않는다. 정확히 맞는 건 합성 데이터의 성질이지 방법이 좋아서가 아니다.

진짜 잡음을 넣고 다시 하면 이렇게 나온다. 잡음만 있었던 열다섯 개 스트림 기준이다.

RMS error, measured : 4.7201
RMS error, estimate : 2.3543

두 배쯤 줄었다. 이 네트워크의 중복성이 그만큼 값을 한다. 다만 이 비교를 하려면 참값을 알아야 하는데 실제 공정에는 그런 열이 없다. 그래서 실제 데이터를 두고 정확도가 좋아졌다는 말은 하지 않는다. 할 수 있는 말은 수지가 닫힌다는 것, 그리고 공분산이 맞다면 추정치의 분산이 줄어든다는 것까지다.

2022년과 2026년

2022년 2026년
엑셀 해찾기 → GEKKO 닫힌 형태 가중최소제곱. GEKKO는 비교용으로 보존
GEKKO 원격 solve (공개 서버) 오프라인. numpyscipy
모든 센서에 같은 σ σ가 가정임을 명시. 이질적인 경우도 같이 보여줌
ν를 n−1 / n−2로 고정 ν = rank(A R Aᵀ), 문제마다 다시 계산
배열 하나 반환 공분산·잔차·p값·관측가능성을 담은 결과 객체
식별과 재구성이 섞이고 실패하면 AttributeError 세 단계 분리, 실패도 정상 반환값
"노이즈 보정" 모델 제약하의 추정. 가정을 나열
노트북만 테스트된 패키지, 실행 스크립트, 노트북, CI

GEKKO를 썼던 이유

엑셀 해찾기에서 옮겨오는 입장에서 GEKKO는 합리적인 선택이었다. 목적함수와 제약식을 종이에 쓰던 모양 그대로 쓰고, bounds나 비선형 수지로 확장하기 쉽고, 동적 추정이나 MHE로 가는 학습 경로가 이어진다. IPOPT가 수치를 알아서 처리한다.

다만 이 문제는 선형이고 등식 제약만 있다. 닫힌 해가 더 짧고 정확하다. 해석할 solver 상태도 없고 설치할 의존성도 없다. 2022년 구현은 archive/2022-study/legacy_gekko.py에 그대로 있다. 2026년에 손댄 곳은 호환 문제 세 군데뿐이고 파일 docstring과 아카이브 README에 적어뒀다. 그중 하나는 기본값이 모델을 공개 서버로 보내서 GEKKO(remote=False)로 바꾼 것이다. 수학적·구조적 결함은 전부 그 안에 남아 있고 지금도 테스트가 붙잡고 있다.

tests/test_legacy_equivalence.py가 다섯 가지 데이터로 둘을 비교한다.

경우 최대 절대차 목적함수 (GEKKO / 닫힌 해)
편향 있음 (x6 +130) 4.5e-09 348.194281 / 348.194281
깨끗한 데이터 2.0e-08 0.000000 / 0.000000
잡음 있음 4.5e-09 10.415016 / 10.415016
이질적 σ 8.3e-08 1604.305997 / 1604.305997

2022년 모델은 최적화 문제를 제대로 세우고 있었다. 손봐야 했던 건 그 주변의 통계였다.

실행

git clone https://github.com/Kyuhan1230/DataReconciliation
cd DataReconciliation

uv venv && uv pip install -e ".[dev]"

uv run python examples/flow_network.py   # 전체 스터디를 출력
uv run pytest -q                         # 78개, 4초쯤

uv 없이 쓰려면 python -m venv .venv 로 가상환경을 만들고 pip install -e ".[dev]" 를 쓰면 된다.

numpyscipy만 있으면 된다. solver도 네트워크 접속도 계정도 필요 없다. 2022년 GEKKO 비교까지 돌리려면 uv pip install -e ".[dev,legacy]".

notebooks/walkthrough.ipynb는 같은 내용을 그림과 함께 보여주고 빈 커널에서 위에서 아래까지 다시 실행된다. API는 docs/method-and-assumptions.md에 정리했다.

가정, 그리고 무너지는 조건

이 추정량이 최적인 조건은 실제 플랜트가 대체로 깨뜨린다. 자주 깨지는 순서대로 적었다.

가정 깨뜨리는 것 대신 할 일
정상상태 움직이는 레벨, 긴 체류시간, 제품 전환 안정 구간을 고르거나 칼만 필터·MHE로
동시 계측 분석기 지연, 실험실 주기, historian 압축 시간정렬부터. solver로 해결되지 않는다
모델이 완전함 누설, purge, 미계측 recycle 잔차가 센서가 아니라 모델 탓일 수 있다
공분산을 안다 데이터시트에서 베낀 σ 하나 교정 이력에서 σ를 뽑고 상관도 확인
Gross error가 하나 여러 계기가 동시에 드리프트 masking과 swamping. 이 절차는 안 통한다
중복성이 충분함 계측이 성긴 네트워크 중복성 없는 계측값은 검증 자체가 불가능

그리고 결과를 어떻게 보고할지를 결정하는 조건 하나. 수지가 잘 닫혔다고 값이 정확해지지는 않는다. 보정은 설계상 수지를 닫는다. 그러니 폐합이 좋아졌다는 걸 정확도의 근거로 쓰면 순환논증이다. 실험실 분석이나 교정 기록 같은 외부 기준이 있어야 한다.

여기 없는 것들. bounds, bilinear·비선형 수지, 동특성, 정상상태 판정, 시간정렬, 다중 gross error, robust estimator. 자세한 내용은 docs/limitations.md에 있다.

저장소 구조

src/process_dr_example/   linear.py 추정량, diagnostics.py 검정
examples/  notebooks/     실행 스크립트, 데이터, walkthrough.ipynb
tests/                    78개 (GEKKO 포함 85개)
docs/  assets/            문서, 도면
archive/2022-study/       엑셀 워크북, 원본 노트북, GEKKO 코드

연구 맥락 · 방법과 가정 · 한계 · 참고문헌 · 2022년 원본과 결함 목록

커밋 이력은 2022년 2월까지 이어지고 다시 쓰지 않았다. legacy-2023 태그가 개편 직전 지점을 가리킨다.

참고문헌

2022년 연구는 Martini 외(2014)의 serial elimination, Romagnoli & Sánchez(1999), Miao 외(2009)를 보고 했다. 이번에 Mah, Stanley & Downing(1976), 투영을 쓴 Crowe 외(1983), Mah & Tamhane(1982), Narasimhan & Mah(1987), 그리고 자유도가 왜 개수가 아니라 rank인지를 다룬 Crowe(1989)를 더했다. DOI를 포함한 전체 목록은 docs/references.md에 있다.

라이선스

MIT. LICENSE 참고.


In English

A worked example of steady-state process data reconciliation on a 16-flow network, carried from a 2022 Excel Solver experiment to a tested Python implementation. One example studied closely, not a general-purpose library. The 2022 test statistic was right; its degrees of freedom were not, and the defect stayed invisible for four years because the demo still passed. Reconciled values are model-constrained estimates, not ground truth.

Full text: README.en.md.

About

A revisited study of steady-state process data reconciliation: a 2022 Excel/GEKKO experiment preserved, with a transparent and tested Python implementation. Reconciled values are model-constrained estimates, not ground truth.

Topics

Resources

Stars

0 stars

Watchers

1 watching

Forks

Releases

Packages

Contributors

Languages