## Fusion of high-quality and low-quality data - GMM-based implementation
![diagram](/figs/2021JUL21/diagram-20210721.png)
### Model formulation
Suppose $`\mathbf{X}^{H}, \mathbf{X}^{L}`$ are $`N \times V`$ feature matrices (e.g. connectivity between $`N`$ thalamus voxels and $`V`$ whole brain voxels). Note that they can have different dimensions in practice. To keep notations uncluttered, we suppose the number of voxels in high- and low-quality images are the same for a given subject. Now we assume $`\mathbf{X}^{H}, \mathbf{X}^{L}`$ share the same latent variable $`Y`$, which is a $`N \times K`$ binary matrix representing the voxels' classes.
For a single voxel, suppose $`\mathbf{y}_{n} \sim \text{multinomial}(\mathcal{\pi})`$, and $`p(\mathbf{x}^{L}_{n}|y_{nk}=1) = \mathcal{N}(\mu_{k}, \Sigma_{k}^{L})`$. To use high-quality data to inform the inference on low-quality data, we assume $`p(\mathbf{x}^{H}_{n}|y_{nk}=1, \mathbf{U}) = \mathcal{N}(\mathbf{U}\mathbf{x}^{H}_{n}|\mu_{k}, \Sigma_{k}^{H})`$ where $`\mathbf{U}^{T}\mathbf{U} = \mathbf{I}`$. The complete log-likelihood can be written as
```math
\log p(\mathbf{x}^{H}_{n}, \mathbf{x}^{L}_{n}, \mathbf{y}_{n}|\mathbf{U},...\mathbf{\pi}, \mathbf{\mu}_{k},...\mathbf{\Sigma}^{H}_{k},...\mathbf{\Sigma}^{L}_{k},...) = \prod_{k=1}^{K}(\mathcal{N}(\mathbf{x}^{L}_{n}|\mu_{k},\Sigma_{k}^{L})\mathcal{N}(\mathbf{Ux}^{H}_{n}|\mu_{k},\Sigma_{k}^{H}))^{y_{nk}}
```
The marginal distribution of $`\mathbf{x}_{n}^{L}, \mathbf{x}_{n}^{H}`$ is
```math
\log p(\mathbf{x}^{H}_{n}, \mathbf{x}^{L}_{n} | \mathbf{U},...\mathbf{\pi}, \mathbf{\mu}_{k},...\mathbf{\Sigma}^{H}_{k},...\mathbf{\Sigma}^{L}_{k},...)=\sum_{k=1}^{K}\pi_{k}\mathcal{N}(\mathbf{x}^{L}_{n}|\mu_{k},\Sigma_{k}^{L})\mathcal{N}(\mathbf{Ux}^{H}_{n}|\mu_{k},\Sigma_{k}^{H})
```
In summary, in addition to finding the the hyper-parameters $`\pi, \mu, \Sigma_{k}^{H}, \Sigma^{L}_{k}`$, we want to estimate a transformation matrix $`\mathbf{U}`$ such that $`\mathbf{UX}^{H}`$ is as close to $`\mathbf{X}^{L}`$ as possible (or vice versa).
### Pseudo code - Algorithm 1. EM for the Fusion of GMMs
1. Run K-means clustering on the high-quality data to generate the assignment of the voxels $`R^{(0)}`$.
2. Initialise the means $`\mu_{k}^{L}`$, $`\mu_{k}^{H}`$, covariances $`\Sigma_{k}^{L}`$, $`\Sigma_{k}^{H}`$, and mixing coefficients $`\pi_k`$ using the K-means assignment $`R^{(0)}`$, and evaluate the initial likelihood.
3. Initialise the transformation matrix $`\mathbf{U} = \mathbf{MN}^{T}`$, where $`\mathbf{MDN}^{T}`$ is the SVD of $`\sum_{k=1}^{K}\mu_{k}^{H}(\mu_{k}^{L})^{T}`$.
4. For iteration = $`1, 2, ...`$, do
- **E-step.** Evaluate the responsibilities using the current parameter values
- $`\gamma(y_{nk}) = \frac{\pi_{k}\mathcal{N}(\mathbf{x}^{L}_{n} | \mu_{k}^{L}, \Sigma_{k}^{L})\mathcal{N}(\mathbf{Ux}^{H}_{n} | \mu_{k}^{L}, \Sigma_{k}^{H})}{\sum_{j=1}^{K}\pi_{j}\mathcal{N}(\mathbf{x}^{L}_{n} | \mu_{k}^{L}, \Sigma_{k}^{L})\mathcal{N}(\mathbf{Ux}^{H}_{n} | \mu_{k}^{L}, \Sigma_{k}^{H})}`$
- **M-step.** Re-estimate the parameters using the current responsibilities by setting the derivatives to zero
- $`\mu_{k}^{L} = \frac{1}{N_{k}}((\Sigma^{H}_{k})^{-1} + (\Sigma^{L}_{k})^{-1} )^{-1}\sum_{n=1}^{N}\gamma(y_{nk})((\Sigma_{k}^{H})^{-1}\mathbf{Ux}^{H}_{n} + (\Sigma_{k}^{L})^{-1}\mathbf{x}_{n}^{L} )`$
- $`\Sigma_{k}^{L} = \frac{1}{N_{k}}\sum_{n=1}^{N}\gamma(y_{nk})(\mathbf{x}^{L}_{n} - \mathbf{\mu}_{k}^{L})(\mathbf{x}^{L}_{n} - \mathbf{\mu}_{k}^{L})^{T}`$
- $`\Sigma_{k}^{H} = \frac{1}{N_{k}}\sum_{n=1}^{N}\gamma(y_{nk})(\mathbf{Ux}^{H}_{n} - \mathbf{\mu}_{k}^{L})(\mathbf{Ux}^{H}_{n} - \mathbf{\mu}_{k}^{L})^{T}`$
- $`\pi_k = \frac{N_{k}}{N}`$
- $`\mathbf{U}=\mathbf{MN}^{T}`$ where $`\mathbf{MDN}^{T}`$ is the svd of $`\sum_{k=1}^{K}\gamma(y_nk)\mu_{k}^{L}(\mathbf{x}_{n}^{H})^{T}(\sum_{n=1}^{N}\mathbf{x}_{n}^{H}(\mathbf{x}_{n}^{H})^{T})`$
- Evaluate the likelihood and check for convergence.
5. Using $`\mu_{k}^{L}, \Sigma_{k}^{L}, \pi_{k}`$ to assignment unseen low-quality data points.
### Simulation results
#### We considered three scenarios
##### I. Low-quality data noisier than the high-quality data
We simulate the case where the features of low-quality data are noiser than those of the high-quality data. The number of informative features remains the same, however.
```julia
noise_level = 10
d = 3
# the high- and low-quality share the same cluster centroid
# there are two clusters, and d features
XHmean = hcat(randn(Float32, d), randn(Float32, d))
XLmean = copy(XHmean)
n_samples = 1000
# there are two clusters
class = rand(1:2, n_samples) # this is ytrain
# pre-allocate
XHtrain, XLtrain = [Matrix{Float32}(undef, d, n_samples) for _ ∈ 1:2]
# XLtrain is noisier by a factor of noise_level
for c ∈ [1, 2]
XHtrain[:, findall(x -> x==c, class)] .= rand(MvNormal(XHmean[:, c], 0.05f0 * I), count(x -> x==c, class))
XLtrain[:, findall(x -> x==c, class)] .= rand(MvNormal(XLmean[:, c], 0.05f0 * noise_level * I), count(x -> x==c, class))
end
```
##### II. Low-quality data noisier than the high-quality data with less informative features
In this scenario, low-quality data has less informative features
```julia
# the high- and low-quality share the same cluster centroid
# there are two clusters, and d features
XHmean = hcat(randn(Float32, d), randn(Float32, d))
XLmean = copy(XHmean)
# 50% of the original features are non-informative
XLmean[rand(1:d, Int(round(d * 0.5))), :] .= 0.0f0
n_samples = 1000
# there are two clusters
class = rand(1:2, n_samples) # this is ytrain
# pre-allocate
XHtrain, XLtrain = [Matrix{Float32}(undef, d, n_samples) for _ ∈ 1:2]
# XLtrain is noisier by a factor of noise_level
for c ∈ [1, 2]
XHtrain[:, findall(x -> x==c, class)] .= rand(MvNormal(XHmean[:, c], 0.05f0 * I), count(x -> x==c, class))
XLtrain[:, findall(x -> x==c, class)] .= rand(MvNormal(XLmean[:, c], 0.05f0 * noise_level * I), count(x -> x==c, class))
end
```
##### III. Low-quality data noiser than the high-quality data with 10% outliers
```julia
# the high- and low-quality share the same cluster centroid
# there are two clusters, and d features
XHmean = hcat(randn(Float32, d), randn(Float32, d))
XLmean = copy(XHmean)
n_samples = 1000
# there are two clusters
class = rand(1:2, n_samples) # this is ytrain
# pre-allocate
XHtrain, XLtrain = [Matrix{Float32}(undef, d, n_samples) for _ ∈ 1:2]
# XLtrain is noisier by a factor of noise_level
for c ∈ [1, 2]
XHtrain[:, findall(x -> x==c, class)] .= rand(MvNormal(XHmean[:, c], 0.05f0 * I), count(x -> x==c, class))
XLtrain[:, findall(x -> x==c, class)] .= rand(MvNormal(XLmean[:, c], 0.05f0 * noise_level * I), count(x -> x==c, class))
end
# 10% of the samples are outliers
XLtrain[:, rand(1:n_samples, Int(round(n_samples / 10)))] .= randn(d, Int(round(n_samples / 10))) .* 2
```
#### Results
Note that "High+Low" and "Low" are initialised from the same points.
- d = 3
![res1](/figs/2021JUL21/d3.svg)
- d = 10
![res2](/figs/2021JUL21/d10.svg)