Restructure package: descriptive study documentation and clean layout
This commit is contained in:
Executable
+128
@@ -0,0 +1,128 @@
|
||||
"""Real-data (digits) Fig. 5 generator: downstream classification accuracy vs SNR
|
||||
for HIGH/LOW/MIX, parallel to the synthetic Fig. 2.
|
||||
|
||||
Curves per panel:
|
||||
- OFDMA [division], SFDMA [feature div.], NOMA-SIC (analytical baselines)
|
||||
- UWCA (analytical) : oracle-beta cross-attention (relevance SUPPLIED) -- dotted
|
||||
- UWCA w/o MAML : decoder TRAINED on real digits, no meta-learning (from realdata_train.json)
|
||||
- UWCA w/ MAML : decoder TRAINED on real digits with MAML (proposed) -- hollow circles
|
||||
|
||||
Trained curves are read from results/realdata_train.json (produced by
|
||||
revision_realdata_train.py); the analytical / baseline curves are recomputed here
|
||||
so they share one Monte-Carlo setting. Saves results/fig_realdata_c.pdf.
|
||||
"""
|
||||
import json, numpy as np
|
||||
import matplotlib; matplotlib.use('Agg')
|
||||
import matplotlib.pyplot as plt
|
||||
from sklearn.datasets import load_digits
|
||||
|
||||
RNG = np.random.default_rng(7)
|
||||
U, D = 4, 64
|
||||
DPU = D // U
|
||||
MASKS = np.zeros((U, D))
|
||||
for u in range(U):
|
||||
MASKS[u, u*DPU:(u+1)*DPU] = 1.0
|
||||
NOMA_POWER = np.array([0.40, 0.30, 0.20, 0.10])
|
||||
TAU = 0.45
|
||||
|
||||
X, y = load_digits(return_X_y=True)
|
||||
X = X.astype(np.float64); X = X - X.mean(0, keepdims=True)
|
||||
X = X / (np.linalg.norm(X, axis=1, keepdims=True) + 1e-8)
|
||||
by_class = {c: X[y == c] for c in range(10)}
|
||||
PROTO = np.stack([by_class[c].mean(0) for c in range(10)])
|
||||
PROTO = PROTO / (np.linalg.norm(PROTO, axis=1, keepdims=True) + 1e-8)
|
||||
|
||||
def _norm(E): return E / (np.linalg.norm(E, axis=-1, keepdims=True) + 1e-8)
|
||||
|
||||
def se_channel(E, snr):
|
||||
n = E.shape[0]; Ytx = (E * MASKS[None]).sum(1)
|
||||
h = np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2)*np.sqrt(0.5)
|
||||
nstd = np.sqrt(float(np.mean(Ytx**2))/(10**(snr/10)))
|
||||
return h*Ytx[:, None, :] + RNG.standard_normal((n, U, D))*nstd
|
||||
|
||||
def ofdma(Y): return np.stack([_norm(Y[:, u, :]*MASKS[u]) for u in range(U)], 1)
|
||||
|
||||
def sfdma(E, snr):
|
||||
"""Full-band, orthogonal semantic-subspace division (block basis)."""
|
||||
n = E.shape[0]
|
||||
h = np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2)*np.sqrt(0.5)
|
||||
X_ = E * MASKS[None]
|
||||
yv = (h * X_).sum(1)
|
||||
yv = yv + RNG.standard_normal((n, D))*np.sqrt(float(np.mean(yv**2))/(10**(snr/10)))
|
||||
return np.stack([_norm((yv * MASKS[u])/(h[:, u, :]+1e-8)) for u in range(U)], 1)
|
||||
|
||||
def noma_ch(E, snr):
|
||||
n = E.shape[0]
|
||||
h = np.sqrt(RNG.standard_normal((n, U, 1))**2 + RNG.standard_normal((n, U, 1))**2)*np.sqrt(0.5)
|
||||
yv = (E*np.sqrt(NOMA_POWER)[None, :, None]*h).sum(1)
|
||||
yv = yv + RNG.standard_normal((n, D))*np.sqrt(float(np.mean(yv**2))/(10**(snr/10)))
|
||||
return yv, h
|
||||
|
||||
def noma_sic(yv, h):
|
||||
n = yv.shape[0]; Eh = np.zeros((n, U, D)); res = yv.copy()
|
||||
for u in range(U):
|
||||
Eh[:, u, :] = _norm(res/(h[:, u, :]+1e-8)); res -= h[:, u, :]*np.sqrt(NOMA_POWER[u])*Eh[:, u, :]
|
||||
return Eh
|
||||
|
||||
def uwca_oracle(Y, bmc):
|
||||
"""Analytical UWCA: oracle-beta cross-attention (relevance supplied)."""
|
||||
R = Y[:, :, None, :]*MASKS[None, None]
|
||||
a = bmc.copy(); np.fill_diagonal(a, 1.0); a /= a.sum(1, keepdims=True)+1e-8
|
||||
ctx = np.einsum('ui,buid->bud', a, R)
|
||||
return np.stack([_norm(ctx[:, u, :]) for u in range(U)], 1)
|
||||
|
||||
SCEN = {'HIGH': [3, 3, 3, 3], 'LOW': [0, 1, 7, 4], 'MIX': [3, 3, 8, 1]}
|
||||
def sample(n, ca): return np.stack([by_class[ca[u]][RNG.integers(0, len(by_class[ca[u]]), n)] for u in range(U)], 1)
|
||||
def emp_beta(ca, n=4000):
|
||||
E = sample(n, ca); return np.einsum('nud,nvd->uv', E, E)/n
|
||||
def acc(Eh, ca):
|
||||
pred = np.einsum('nud,cd->nuc', _norm(Eh), PROTO).argmax(-1)
|
||||
return (pred == np.array(ca)[None, :]).mean()
|
||||
|
||||
SNR = np.arange(0, 21, 2)
|
||||
trained = json.load(open('results/realdata_train.json')) # trained UWCA w/ and w/o MAML
|
||||
|
||||
# --- recompute analytical / baseline accuracy (shared MC) ---
|
||||
ana = {s: {m: [] for m in ['OFDMA', 'SFDMA', 'NOMA-SIC', 'UWCA (analytical)']} for s in SCEN}
|
||||
for s, ca in SCEN.items():
|
||||
bm = emp_beta(ca); bmc = bm.copy(); np.fill_diagonal(bmc, 0.0); bmc = np.clip(bmc, 0, None)
|
||||
for snr in SNR:
|
||||
ao = asf = an = au = 0.0; nmc = 200
|
||||
for _ in range(nmc):
|
||||
E = sample(64, ca)
|
||||
ao += acc(ofdma(se_channel(E, snr)), ca)
|
||||
asf += acc(sfdma(E, snr), ca)
|
||||
yv, h = noma_ch(E, snr); an += acc(noma_sic(yv, h), ca)
|
||||
au += acc(uwca_oracle(se_channel(E, snr), bmc), ca)
|
||||
ana[s]['OFDMA'].append(ao/nmc); ana[s]['SFDMA'].append(asf/nmc)
|
||||
ana[s]['NOMA-SIC'].append(an/nmc); ana[s]['UWCA (analytical)'].append(au/nmc)
|
||||
|
||||
# --- plot: downstream accuracy, 1 row x 3 cols ---
|
||||
COL = {'OFDMA': '#546E7A', 'SFDMA': '#9C27B0', 'NOMA-SIC': '#E65100',
|
||||
'UWCA (analytical)': '#1565C0', 'UWCA w/o MAML': '#2E7D32', 'UWCA w/ MAML': '#1565C0'}
|
||||
fig, ax = plt.subplots(1, 3, figsize=(11, 3.4))
|
||||
betas = {s: emp_beta(SCEN[s])[~np.eye(U, dtype=bool)].mean() for s in SCEN}
|
||||
for j, s in enumerate(['HIGH', 'LOW', 'MIX']):
|
||||
a = ax[j]
|
||||
a.plot(SNR, ana[s]['OFDMA'], 's--', color=COL['OFDMA'], lw=2, ms=5, label='OFDMA [division]')
|
||||
a.plot(SNR, ana[s]['SFDMA'], 'v:', color=COL['SFDMA'], lw=2, ms=5, mfc='none', label='SFDMA [feature div.]')
|
||||
a.plot(SNR, ana[s]['NOMA-SIC'], '^-.', color=COL['NOMA-SIC'], lw=2, ms=5, label='NOMA-SIC')
|
||||
a.plot(SNR, ana[s]['UWCA (analytical)'], ':', color=COL['UWCA (analytical)'], lw=2.4, label='UWCA (analytical)')
|
||||
a.plot(SNR, trained[s]['UWCA w/ MAML'], 'o-', color=COL['UWCA w/ MAML'], lw=1.6, ms=6, mfc='none', mew=1.6, label='UWCA (trained)')
|
||||
a.set_ylim(0.1, 1.02); a.set_xlim(0, 20); a.grid(alpha=.3); a.set_box_aspect(0.85)
|
||||
a.set_xlabel('SNR (dB)', fontsize=10)
|
||||
a.text(0.5, -0.34, f"({chr(97+j)}) {s} ($\\hat\\beta_{{u,v}}\\approx{betas[s]:.2f}$)",
|
||||
transform=a.transAxes, ha='center', fontsize=11)
|
||||
if j == 0:
|
||||
a.set_ylabel('Downstream accuracy', fontsize=10)
|
||||
ax[0].legend(fontsize=7.0, loc='upper left', bbox_to_anchor=(0.46, 0.37),
|
||||
bbox_transform=ax[0].transAxes, framealpha=0.9, borderaxespad=0.0)
|
||||
fig.tight_layout()
|
||||
fig.savefig('results/fig_realdata.pdf', bbox_inches='tight')
|
||||
fig.savefig('results/fig_realdata.png', dpi=140, bbox_inches='tight')
|
||||
fig.savefig('results/fig_realdata_c.pdf', bbox_inches='tight')
|
||||
print('saved results/fig_realdata_c.pdf')
|
||||
for s in SCEN:
|
||||
print(f"[{s}] @20dB OFDMA={ana[s]['OFDMA'][-1]:.3f} SFDMA={ana[s]['SFDMA'][-1]:.3f} "
|
||||
f"NOMA={ana[s]['NOMA-SIC'][-1]:.3f} UWCA-ana={ana[s]['UWCA (analytical)'][-1]:.3f} "
|
||||
f"UWCA-MAML(tr)={trained[s]['UWCA w/ MAML'][-1]:.3f}")
|
||||
Reference in New Issue
Block a user