A dynamic risk prediction framework for Alzheimer's disease and related dementias with interpretability.
The 2 matches
- [1] § Methods › Deep learning models ↔ i_gru_d.py, lines 244–365 · score 0.72 · context vector, weight matrix, summed, visit, dimension, tensor
- [2] § Methods › Deep learning models ↔ i_gru_d.py, lines 6–155 · score 0.62 · dropout ratio, neuron, computational, beta, batch, GRU
Paper
Loaded from Europe PMC by your browser, not stored by OSCR: doi.org · Europe PMC
The paper is loaded when this pane is shown.
The authors' code
Python · 395 lines · 18 KB · other · 2 matches
- import torch
- import math
- import numpy as np
- class GRUD_cell(torch.nn.Module):
- def __init__(self,
- input_size, #take an integer, the number of input features
- hidden_size, #take an integer, the number of neurons in hidden layer
- output_size, #take an integer, the number of output features
- x_mean=0, #x_mean should be a vector (rather than a scalar as the default value here) containing the empirical mean value for each feature
- update_x_mean=True,
- non_neg=None,
- #bias=True, #always include bias vector for z,r,h
- #bidirectional=False, #not applicable in this study since in practice we won't know future data points
- #dropout_type='mloss',
- dropoutratio=0, #the dropout ratio for between timesteps of hidden layers.
- dtypearg=torch.float64,
- usedevice='cuda:0'
- ):
- torch.set_default_dtype(dtypearg)
- cuda_available = torch.cuda.is_available() #return True if NVIDIA available
- device = torch.device(usedevice if cuda_available else 'cpu')
- self.dtype=dtypearg
- self.device=device
- super(GRUD_cell,self).__init__()
- self.input_size=input_size
- self.hidden_size=hidden_size
- self.output_size=output_size
- #as noted by the author, register_buffer is used because it automatically load data to GPU,if any. However, this also prohibit x_mean from updating
- if update_x_mean:
- self.x_mean = torch.nn.Parameter(torch.tensor(x_mean.clone(), dtype=dtypearg, requires_grad=True, device=device))
- else:
- self.register_buffer('x_mean', torch.tensor(x_mean.clone(), dtype=dtypearg, device=device))
- #self.bias=bias
- self.dropoutratio=dropoutratio
- self.dropoutlayer=torch.nn.Dropout(p=dropoutratio) #dropout must be defined within __init__ so switch between eval() and train() model effectively turn off/on dropout
- #self.dropout_type=dropout_type
- #self.bidirectional=bidirectional
- #num_directions=2 if bidirectional else 1
- #weight matrix for gamma
- self.w_dg_x=torch.nn.Linear(input_size,input_size,bias=True,device=device)
- self.w_dg_h=torch.nn.Linear(input_size,hidden_size,bias=True,device=device)
- #weight matrix for z, update gate
- self.w_xz=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
- self.w_hz=torch.nn.Linear(hidden_size,hidden_size,bias=False,device=device)
- self.w_mz=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
- self.b_z=torch.nn.Parameter(torch.tensor(np.ndarray(hidden_size),dtype=dtypearg,requires_grad=True,device=device))
- #weight matrix for r, reset gate
- self.w_xr=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
- self.w_hr=torch.nn.Linear(hidden_size,hidden_size,bias=False,device=device)
- self.w_mr=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
- self.b_r=torch.nn.Parameter(torch.tensor(np.ndarray(hidden_size),dtype=dtypearg,requires_grad=True,device=device))
- #weight matrix for hidden units
- self.w_xh=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
- self.w_hh=torch.nn.Linear(hidden_size,hidden_size,bias=False,device=device)
- self.w_mh=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
- self.b_h=torch.nn.Parameter(torch.tensor(np.ndarray(hidden_size),dtype=dtypearg,requires_grad=True,device=device))
- #weight matrix for linking hidden layer to final output
- self.w_hy=torch.nn.Linear(hidden_size,output_size,bias=True,device=device)
- self.batchnorm=torch.nn.BatchNorm1d(hidden_size)
- #the hidden state vector, it is the conveying belt that transfer information to the next timestep
- #it will be scaled to batch_size x hidden_size during forward implementation (as the batch_size is unknown here)
- Hidden_State=torch.zeros(hidden_size,dtype=dtypearg,device=device)
- #it is registered in buffer, which prevents it from change. so this is a "stateless" model, which assumes no relatinoship between each training sample
- self.register_buffer('Hidden_State',Hidden_State)
- #a vector containing the last observation
- #it will be scaled to [batch_size, input_size] in the following script
- X_last_obs=torch.zeros(input_size,dtype=dtypearg,device=device)
- self.register_buffer('X_last_obs',X_last_obs)
- self.non_neg=non_neg
- self.reset_parameters()
- def reset_parameters(self): #this reset all weight parameters
- stdv=1.0/math.sqrt(self.hidden_size)
- #for weight in self.parameters(): #note this reset all weights matrix except those registered in buffer
- #torch.nn.init.uniform_(weight, -1 * stdv, stdv)
- for name,weight in self.named_parameters():
- if(name != 'x_mean'): #avoid reset certain weights at the startup
- torch.nn.init.uniform_(weight, -1 * stdv, stdv)
- def setdevice(self,usedevice):
- self.to(usedevice)
- self.device=torch.device(usedevice)
- @property
- def _flat_weights(self): #no idea what this is doing
- return list(self._parameters.values())
- def forward(self, input):
- #determine the device where
- device=self.device #<class 'torch.device'>
- #input has these dimensions [batch_size,"X Mask Delta",feature_size, timestep_size]
- #move input to corresponding device
- if(input.device != device):
- input=input.to(device)
- X=input[:,0,:,:] #X has dimension [batch_size, feature_size, timestep_size]
- Mask=input[:,1,:,:]
- Delta=input[:,2,:,:]
- step_size=X.size(2) #the size of timesteps
- output=None
- h=getattr(self,'Hidden_State') #h is a vector
- x_mean=getattr(self,'x_mean')
- x_last_obsv=getattr(self,'X_last_obs')
- #an empty tensor for holding the output of each timestep
- #the dimensions are [batch_size,timestep_size,output_feature_size]
- output_tensor=torch.empty([X.size(0),X.size(2),self.output_size],
- dtype=X.dtype, device=device)
- #an empty tensor for holding the hidden state of each timestep
- #the dimensions are [batch_size,timestep_size,hidden_size]
- hidden_tensor=torch.zeros([X.size(0),X.size(2),self.hidden_size],
- dtype=X.dtype,device = device)
- #x_tensor holds the original x values, and if not available, those imputed on the fly
- #[batch_size, feature_size, timestep_size]
- x_tensor=torch.zeros([X.size(0),self.input_size,X.size(2)],
- dtype=X.dtype,device = device)
- #iterate over timesteps
- for timestep in range(X.size(2)):
- #squeeze drop the timestep dimension and ends up with [batch_size,feature_size]
- x=X[:,:,timestep] #x has dimension [batch_size, feature_size]
- m=Mask[:,:,timestep]
- d=Delta[:,:,timestep]
- #the gamma vector for filtering x. the dimension is [batch_size,feature_size]
- #each element in gamma_x is a value within (0,1]
- gamma_x=torch.exp(-1*torch.nn.functional.relu(self.w_dg_x(d)))
- #the gamma vector for filtering h. the dimension is [batch_size,hidden_size]
- gamma_h=torch.exp(-1*torch.nn.functional.relu(self.w_dg_h(d)))
- #update x_last_obsv. x_last_obsv is a vector of input_size
- #it is changed to a [batch_size,feature_size] tensor on first run
- #x_last_obsv are all 0 at the beginning, and update to the latest available data
- x_last_obsv=torch.where(m>0,x,x_last_obsv)
- #set nan in x to 0 -- necessary to do this on the training data before input to cell
- #x[torch.isnan(x)]=0 #this kind of operation is strictly not allowed becuase it raises the RuntimeError: one of the variables needed for gradient computation has been modified by an inplace operation
- x=m*x + (1-m)*(gamma_x*x_last_obsv + (1-gamma_x)*x_mean)
- if(not self.non_neg is None):
- x[:,self.non_neg]=torch.nn.functional.softplus(x[:,self.non_neg],beta=100) #when beta is 100, x=0 map to 0.007
- #
- h=gamma_h*h #h is initialized as a vector, and reshaped to [batch_size,hidden_size] on first run
- #z is [batch_size, hidden_size]
- z=torch.sigmoid(self.w_xz(x) + self.w_hz(h) + self.w_mz(m) + self.b_z)
- #r is [batch_size, hidden_size]
- r=torch.sigmoid(self.w_xr(x) + self.w_hr(h) + self.w_mr(m) + self.b_r)
- #h_tilde is [batch_size, hidden_size]
- h_tilde=torch.tanh(self.w_xh(x) + self.w_hh(r*h) + self.w_mh(m) + self.b_h)
- #h is [batch_size, hidden_size]
- h=(1-z)*h + z*h_tilde
- #if using batch normalization, should be added here
- h=self.batchnorm(h)
- if(self.dropoutratio > 0): #remember to use eval() to turn off dropout during evaluation
- h=self.dropoutlayer(h)
- step_output=self.w_hy(h) #[batch_size,output_size]
- step_output=torch.sigmoid(step_output)
- #note the following operations generate hard copy of the right operand, which is important
- output_tensor[:,timestep,:]=step_output #[batch_size,timestep_size,output_size]
- hidden_tensor[:,timestep,:]=h #[batch_size,timestep_size,hidden_size]
- x_tensor[:,:,timestep]=x #[batch_size, feature_size, timestep_size]
- #end of for
- output=(output_tensor,hidden_tensor,x_tensor,Mask)
- return output
- #end of forward
- #end of class
- class GRUD_model(torch.nn.Module):
- def __init__(self,
- input_size,
- hidden_size, #this determines hidden_size of both the first and all stacked layers
- output_size, #the output size of every layer
- num_layers=1,
- x_mean=0, #empirical mean of each input feature
- update_x_mean=True,
- non_neg=None,
- #bias=True,
- #batch_first=False,
- #bidirectional=False,
- #dropout_type='mloss',
- dropoutratio=0,
- dtypearg=torch.float64,
- usedevice='cuda:0'
- ):
- torch.set_default_dtype(dtypearg)
- cuda_available = torch.cuda.is_available()
- device = torch.device(usedevice if cuda_available else 'cpu')
- self.device=device
- super(GRUD_model,self).__init__()
- #first layer is the GRU-D
- self.gru_d=GRUD_cell(input_size=input_size,
- hidden_size=hidden_size,
- output_size=output_size,
- x_mean=x_mean,
- update_x_mean=update_x_mean,
- non_neg=non_neg,
- dropoutratio=dropoutratio,
- dtypearg=dtypearg,
- usedevice=usedevice
- )
- self.num_layers=num_layers
- self.hidden_size=hidden_size
- #stack other layers as regular GRU layer
- if(self.num_layers > 1):
- self.gru_layers=torch.nn.GRU(input_size=hidden_size,
- hidden_size=hidden_size,
- batch_first=True, #necessary because the output from gru_d is [batch_size,timestep,feature], whereas torch.nn.GRU takes [timestep,batch,feature] by default
- num_layers=self.num_layers-1,
- dropout=dropoutratio,
- device=device
- ) #I manually confirmed torch.nn.GRU dropout is sensitive to switch between eval() and training()
- #this is for converting the last layer hidden output to actual output
- self.hidden_to_output=torch.nn.Linear(hidden_size, output_size, bias=True)
- #end of __init__
- def setdevice(self,usedevice):
- self.to(usedevice)
- self.device=torch.device(usedevice)
- self.gru_d.setdevice(usedevice)
- def _flat_weights(self):
- return list(self._parameters.values())
- def forward(self,input):
- #pass through the first gru_d layer
- #determine the device where
- if(hasattr(self,'device')):
- device=self.device #<class 'torch.device'>
- #move input to corresponding device
- if(input.device != device):
- input=input.to(device)
- (real_output,hidden,x_tensor,m_tensor)=self.gru_d(input)
- #real_output is [batch_size,timestep,output_feature]
- #hidden is [batch_size,timestep,hidden_size]
- #x_tensor [batch_size, feature_size, timestep_size]
- if(self.num_layers > 1):
- #pass through the rest of regular gru layers
- #output contains the final layer output for all samples and all timesteps
- #output has shape [batch_size,timestep,hidden_size]
- #hidden has shape [num_layers, batch_size, hidden_size]
- #in this example output[:,timestep-1,:] == hidden, should be all true
- (output,hidden)=self.gru_layers(hidden)
- #covert output of last layer hidden output [batch_size, timestep, hidden_size] to real output [batch_size, timestep, output_feature]
- real_output=self.hidden_to_output(output)
- real_output=torch.sigmoid(real_output)
- hidden=output #hidden now is [batch_size, timestep, hidden_size]
- #end of if
- return(real_output,hidden,x_tensor,m_tensor) #make sure the output shape is same as gru_d
- #end of forward
- #end of class
- class I_GRUD_model(torch.nn.Module):
- def __init__(self,
- input_size, #the input feature size
- hidden_size, #this determines hidden_size of both the first and all stacked layers
- output_size, #the output size of every layer
- num_layers=1,
- x_mean=0, #empirical mean of each input feature
- update_x_mean=True,
- #bias=True,
- #bidirectional=False,
- dropoutratio=0,
- betadropoutratio=0,
- traceback=200,
- non_neg=None,
- dtypearg=torch.float64,
- interpretable_mode=True, #whether turn on interpretable mode
- usedevice='cuda:0'
- ):
- torch.set_default_dtype(dtypearg)
- cuda_available = torch.cuda.is_available() #return True if NVIDIA available
- device = torch.device(usedevice if cuda_available else 'cpu')
- self.dtype=dtypearg
- self.device=device
- super(I_GRUD_model,self).__init__()
- self.input_size=input_size
- self.hidden_size=hidden_size
- self.output_size=output_size
- self.traceback=traceback
- self.interpretable_mode=interpretable_mode
- self.batchnorm=torch.nn.BatchNorm1d(input_size)
- self.grud_model=GRUD_model(input_size,hidden_size,output_size,
- num_layers=num_layers,x_mean=x_mean,
- update_x_mean=update_x_mean,non_neg=non_neg,
- dropoutratio=dropoutratio,dtypearg=dtypearg,
- usedevice=usedevice)
- if(self.interpretable_mode):
- #this following codes are for attention
- self.w_beta=torch.nn.Linear(hidden_size,input_size,bias=False,device=device) #feature wise attention
- self.w_epsilon=torch.nn.Linear(hidden_size, 1, bias=False, device=device) #timestep wise attention
- self.softmax=torch.nn.Softmax(dim=1)
- #weight matrix for linking context vector (attention version only) to final output
- self.w_cy=torch.nn.Linear(input_size,output_size,bias=True,device=device)
- self.betadropoutlayer=torch.nn.Dropout(p=betadropoutratio)
- #end of if
- self.reset_parameters()
- def reset_parameters(self): #this reset all weight parameters
- stdv=1.0/math.sqrt(self.hidden_size)
- #for weight in self.parameters(): #note this reset all weights matrix except those registered in buffer
- #torch.nn.init.uniform_(weight, -1 * stdv, stdv)
- for name,weight in self.named_parameters():
- if(name != 'x_mean'): #avoid reset certain weights at the startup
- torch.nn.init.uniform_(weight, -1 * stdv, stdv)
- def setdevice(self,usedevice):
- self.to(usedevice)
- self.device=torch.device(usedevice)
- self.grud_model.setdevice(usedevice)
- @property
- def _flat_weights(self): #no idea what this is doing
- return list(self._parameters.values())
- def forward(self, input):
- #determine the device where
- device=self.device #<class 'torch.device'>
- #move input to corresponding device
- if(input.device != device):
- input=input.to(device)
- (real_output,hidden,x_tensor,m_tensor)=self.grud_model(input)
- #real_output [batch_size,timestep,output_size]
- #hidden [batch_size, timestep, hidden_size]
- if(not self.interpretable_mode):
- return (real_output,hidden,x_tensor,m_tensor)
- else:
- batch_size=real_output.size(0)
- timestep_size=real_output.size(1)
- #an empty tensor for holding the output of each timestep
- #the dimensions are [batch_size,timestep_size,output_feature_size]
- output_tensor=torch.empty([batch_size,timestep_size,self.output_size],
- dtype=input.dtype, device=device)
- context_tensor=torch.empty([batch_size,timestep_size,self.output_size],
- dtype=input.dtype, device=device)
- #visit level attention has two parts
- #epsilon_tensor is for holding original output [batch_size,timestep_size]
- #alpha_tensor is softmax activation of epsilon, which provides timestep level attention
- epsilon_tensor=torch.zeros([batch_size,timestep_size],
- dtype=input.dtype,device = device)
- #feature level attention [batch,input,timestep]
- beta_tensor=torch.zeros([batch_size,self.input_size,timestep_size],
- dtype=input.dtype,device = device)
- #iterate over timesteps
- for timestep in range(timestep_size):
- h=hidden[:,timestep,:] #[batch, hidden]
- #********************************************#
- #the following codes are RETAIN-like attention mechanism
- #visit level attention
- epsilon=self.w_epsilon(h) #[batch,1]
- epsilon_tensor[:,timestep]=epsilon[:,0] #store epsilon for each timestep
- #softmax epsilon to get alpha attention
- attentionsta=timestep - self.traceback #consider limited timesteps back
- if(attentionsta < 0):attentionsta=0
- alpha=self.softmax(epsilon_tensor[:,attentionsta:(timestep+1)].clone()) #[batch,ts]
- #convert [batch,ts] to [batch,input,ts] for easy muliplication with beta and x
- alpha=alpha[:,None,:] #convert to [batch,1,ts] note! reference only
- alpha=alpha.expand(alpha.shape[0],self.input_size,alpha.shape[2]) #expand to [batch,input,ts] note ! reference only, so now every input feature in a timestep got same alpha
- #feature level attention
- beta=self.w_beta(h) #[batch,input_size]
- beta=5 * torch.tanh(beta/5) #smooth out beta as I found beta maybe super large in certain training fold
- beta=self.betadropoutlayer(beta)
- #beta=self.batchnorm(beta)
- beta_tensor[:,:,timestep]=beta #store beta [batch,input,ts]
- #multiply alpha, beta, and x for all current timesteps
- #!!! clone is necessary
- abx=torch.mul(alpha,torch.mul(beta_tensor[:,:,attentionsta:(timestep + 1)].clone(),x_tensor[:,:,attentionsta:(timestep + 1)].clone())) #[batch,input,ts]
- #context is weighted (already done) average (or sum) of traceback timesteps
- context=torch.sum(abx,dim=2) #average existing timesteps values for each input feature [batch,input]
- #step_output=torch.sigmoid(self.w_cy(context)) #[batch,output]
- context_sum=torch.sum(context,dim=1) #[batch]
- context_tensor[:,timestep,:]=context_sum[:,None] #[batch,timestep,output]
- step_output=torch.sigmoid(context_sum) #[batch]
- step_output=step_output[:,None] #[batch,output]
- output_tensor[:,timestep,:]=step_output #[batch,timestep,output]
- #end of for
- output=(output_tensor,hidden,epsilon_tensor,beta_tensor,x_tensor,context_tensor,m_tensor)
- return output
- #end of if
- #end of forward
- #end of class
- from torch.utils.data import Dataset
- from torch.utils.data import DataLoader
- class datasetgenerator(Dataset):
- def __init__(self,nparray,targetarray,valid_timesteps_array=None):
- assert(nparray.shape[0] == targetarray.shape[0]), "nparray length and target array length not match"
- self.z=None
- if(not valid_timesteps_array is None):
- assert(nparray.shape[0] == len(valid_timesteps_array)), "nparray length and valid timestep array length not match"
- self.z=valid_timesteps_array
- self.x=nparray
- self.y=targetarray
- def __len__(self):
- return(self.x.shape[0])
- def __getitem__(self,idx):
- if(self.z is None):
- return(self.x[idx,:,:,:],self.y[idx])
- else:
- return(self.x[idx,:,:,:],self.y[idx],self.z[idx])
- #end of if
- #end of def
- #end of class
i_gru_d.py at commit 82a40e7, under other · at the source
Overview
- Department of Health Data Science and AI, McWilliams School of Biomedical Informatics, UT Health Houston, Houston, USA
- Department of Clinical and Health Informatics, McWilliams School of Biomedical Informatics, UT Health Houston, Houston, USA
- Department of Internal Medicine, McGovern Medical School, UT Health Houston, Houston, USA
Abstract
The abstract is not reproduced here: the paper's license (CC BY-NC-ND) does not allow it. Read it in the paper, at the publisher or on Europe PMC.
Repositories
Its files are read in the Code ↔ Paper reader above, with 2 matches between paragraphs and lines of code.
PeterChe1990/GRU-D
861fc49d9595424bd7604c5cb1a39ab6c500f652, 25 July 2022Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
12 files
- Generate-sample-data.ipy
nb , Jupyter, 98 lines - Prepare-MIMIC-III-data.i
pynb , Jupyter, 103 lines - Run.ipynb, Jupyter, 160 lines
- data_handler.py, Python, 211 lines
- models.py, Python, 103 lines
- nn_utils/
__init__.py , Python, 11 lines - nn_utils/
activations.py , Python, 23 lines - nn_utils/
callbacks.py , Python, 94 lines - nn_utils/
grud_layers.py , Python, 720 lines - nn_utils/
layers.py , Python, 41 lines - LICENSE, License, 21 lines
- README.md, Text, 53 lines
ruanxiaoyang-ut/gru-d-retain
82a40e73b2a51b7f7a0e30b49ba56b0b0750c67f, 6 October 2025Availability: 1 check, the latest on 28 September 2026: the link answers
- 28 September 2026: the link answers
3 files
- i_gru_d.py, Python, 395 lines, 2 matches
- LICENSE, License, 67 lines
- README.md, Text, 6 lines
Code availability statement
The paper has a code availability statement. Its license (CC BY-NC-ND) does not allow reproducing it here; in short, from what the harvester recognized in it:
- it points to the authors' code: PeterChe1990/
GRU-D , ruanxiaoyang-ut/gru-d-retain
Read it in the paper: doi.org/10.1038/s41746-026-02732-0.
Tracing map
Proposed by the machine: these links were found in the paper and verified at the source, without human review. The map will receive a Zenodo DOI once one of the paper's authors has validated it with their ORCID.
What the map holds:
- 2 repositories of the authors' code, each at its verified commit, with its license and how the link was found in the paper;
- 11 scripts, each with its path and the digest of its content;
- 2 matches between paragraphs of the paper and lines of the code (method lexical-v1);
- neither the text of the paper nor the code itself.
Its JSON (tracing-map.json) is deposited on Zenodo with its DOI once the map is validated.
Data
No dataset and no data link were found in the paper.
Availability statements
The paper has a data availability statement and a code and data availability statement. Its license (CC BY-NC-ND) does not allow reproducing them here; in short, from what the harvester recognized in them:
- they point to the authors' code: PeterChe1990/
GRU-D , ruanxiaoyang-ut/gru-d-retain - they say that the data are available on request
Read them in the paper: doi.org/10.1038/s41746-026-02732-0.
Versions
The history of this record: each version stored by the harvester or made by a correction of its authors or of the maintainers of its code, and what changed in its facts. The texts of the paper (its abstract, its availability statements) are not part of it; versions that changed only those are not listed.
Version 1, 28 September 2026: the first record
Recorded: type, language, journal, volume, issue, pages, dates, 11 authors, 6 keywords, 3 funders, 56 references.
Cite
This paper
Ruan, X., Lu, S., Fu, S., Ahn, J., Chen, F., Li, R., Wen, A., Wang, L., Onyema, E., Tang, V., & Liu, H. (2026). A dynamic risk prediction framework for Alzheimer's disease and related dementias with interpretability. NPJ digital medicine, 9(1), 446. https://
BibTeX
@article{ruan2026dynamic
author = {Ruan, Xiaoyang and Lu, Shuyu and Fu, Sunyang and Ahn, Jaerong and Chen, Fang and Li, Rui and Wen, Andrew and Wang, Liwei and Onyema, Ezenwa and Tang, Victoria and Liu, Hongfang},
title = {{A dynamic risk prediction framework for Alzheimer's disease and related dementias with interpretability}},
journal = {NPJ digital medicine},
year = {2026},
month = may,
volume = {9},
number = {1},
pages = {446},
publisher = {Nature Publishing Group},
issn = {2398-6352},
doi = {10.1038/
url = {https://
pmid = {42141052},
pmcid = {PMC13260447}
}
RIS
TY - JOUR
AU - Ruan, Xiaoyang
AU - Lu, Shuyu
AU - Fu, Sunyang
AU - Ahn, Jaerong
AU - Chen, Fang
AU - Li, Rui
AU - Wen, Andrew
AU - Wang, Liwei
AU - Onyema, Ezenwa
AU - Tang, Victoria
AU - Liu, Hongfang
TI - A dynamic risk prediction framework for Alzheimer's disease and related dementias with interpretability
T2 - NPJ digital medicine
J2 - NPJ Digit Med
PY - 2026
DA - 2026/
VL - 9
IS - 1
SP - 446
SN - 2398-6352
PB - Nature Publishing Group
DO - 10.1038/
UR - https://
LA - en
ER -
CSL-JSON
{
"id": "10.1038/
"type": "article-journal",
"title": "A dynamic risk prediction framework for Alzheimer's disease and related dementias with interpretability",
"container-title": "NPJ digital medicine",
"author": [
{
"family": "Ruan",
"given": "Xiaoyang"
},
{
"family": "Lu",
"given": "Shuyu"
},
{
"family": "Fu",
"given": "Sunyang"
},
{
"family": "Ahn",
"given": "Jaerong"
},
{
"family": "Chen",
"given": "Fang"
},
{
"family": "Li",
"given": "Rui"
},
{
"family": "Wen",
"given": "Andrew"
},
{
"family": "Wang",
"given": "Liwei"
},
{
"family": "Onyema",
"given": "Ezenwa"
},
{
"family": "Tang",
"given": "Victoria"
},
{
"family": "Liu",
"given": "Hongfang"
}
],
"container-title-short":
"volume": "9",
"issue": "1",
"page": "446",
"DOI": "10.1038/
"PMID": "42141052",
"PMCID": "PMC13260447",
"ISSN": "2398-6352",
"publisher": "Nature Publishing Group",
"URL": "https://
"language": "en",
"issued": {
"date-parts": [
[
2026,
5,
15
]
]
}
}
The tracing map gets a citation of its own once an author has validated it and it has a DOI.
Similar papers
The papers with a page that share the most with this one: the tools found in their code, their categories, datasets, cited references and authors, the rarest counting most.
- [1] doi:10.1093/jnen/nlaf152 [code]
- Clinical and pathologic correlations of machine learning quantification of Aβ deposits across 3 brain regions of decedents with Alzheimer disease.Journal: Journal of neuropathology and experimental neurologyIn common: Keras, TensorFlow, PyTorch, 2 other tools, Alzheimer's / dementia, clinical / translational
- [2] doi:10.1162/imag.a.1164 [code]
- Bias and generalizability of brain age prediction models: A multi-cohort evaluation with anatomical and interpretability insights.Journal: Imaging neuroscience (Cambridge, Mass.)In common: Keras, TensorFlow, PyTorch, 2 other tools, Alzheimer's / dementia
- [3] doi:10.1038/s41598-026-48666-1 [code]
- Multimodal survival analysis of glioblastoma using whole-slide histopathology, gene expression, clinical variables and language-model-derived mutation features.Journal: Scientific reportsIn common: Keras, TensorFlow, PyTorch, 2 other tools, clinical / translational
- [4] doi:10.1038/s42003-026-10957-8 [code]
- Brain defence by the extracellular matrix protein Cochlin.Journal: Communications biologyIn common: Keras, TensorFlow, PyTorch, 2 other tools
- [5] doi:10.1126/sciadv.aee6952 [code]
- Wafer-scale SOT-MRAM for analog crossbar array applications.Journal: Science advancesIn common: Keras, TensorFlow, PyTorch, 2 other tools
- [6] doi:10.1126/sciadv.aed3650 [code]
- Truthful visualizations for mass spectrometry imaging enable high-spatial-resolution interactive &
lt;i& gt;m/ z& lt;/ i& gt; mapping and exploration. Journal: Science advancesIn common: Keras, TensorFlow, PyTorch, 2 other tools - [7] doi:10.1371/journal.pcbi.1014615 [code]
- Toward reliable machine learning models for neural circuit inference: A diagnostic study of CNNs on spike trains.Journal: PLoS computational biologyIn common: Keras, TensorFlow, PyTorch, 2 other tools
- [8] doi:10.1002/pro.70695 [code]
- MIF-MAPMS: Enhancing identification of myelin autoantigenic peptides in multiple sclerosis through multimodal information fusion.Journal: Protein science : a publication of the Protein SocietyIn common: Keras, TensorFlow, PyTorch, 2 other tools
- [9] doi:10.1371/journal.pcbi.1014571 [code]
- SynAPSeg: A novel dataset and image analysis framework for deep learning-based synapse detection and quantification.Journal: PLoS computational biologyIn common: Keras, TensorFlow, PyTorch, 2 other tools
- [10] doi:10.1364/boe.605322 [code]
- Generalized plaque digitization framework for multi-dimensional mesoscopic images.Journal: Biomedical optics expressIn common: Keras, TensorFlow, PyTorch, 2 other tools
Contribute
The authors of this paper can claim it, correct its record and validate its tracing map, and the maintainers of its code (its owner, or a public member of its organization) correct what it says of their repository; anyone signed in can ask for its removal. Every request goes to OSCR's own machine, which answers it; your account page follows them.
Sign in with ORCID to claim this paper as one of its authors, correct its record or validate its tracing map: when the paper's metadata lists your ORCID iD, you are recognized at once. Maintainers of its code: sign in with GitHub, then claim the repository on your account page.
Claim this paper
Correct its record
Say what each link of this record is, remove the ones that are not the paper's, add the ones that are missing. The correction becomes a new version of the record, in its Versions section.
Validate its tracing map
You validate the map as this page shows it: 2 repositories of the authors' code, each at its verified commit and with its license, 11 scripts, and 2 matches between paragraphs and code (see the Code and Map sections). It then receives a DOI on Zenodo, with you (your ORCID iD) and OSCR as its creators; the code itself is not deposited.
The map's fingerprint: sha256:3f31dc3f3f8e1645…
Add the badge to its README
The badge links the code to this page. Copy one of these into the README of the paper's code: only you decide where it goes, and nothing is changed for you.
Markdown
[, paste the snippet at the top, then “Commit changes…” and, to review it first, “Create a new branch and start a pull request”. You open the pull request; OSCR asks for no permission.
Request its removal
To ask OSCR to remove this record, the copies of its authors' scripts or its tracing map, use the removal request page: signed in, you say who you are, what to remove and why, then review and confirm the request. Published rules decide every request (how).
Discussion, reproductions, activity
Discussion: questions and error reports about this paper and its code, from signed-in readers and its authors. It opens with sign-in.
Reproductions: reports from readers who ran the authors' code: what they reproduced, with which environment, commit and data. It opens with sign-in.
Activity: what happens around this paper: new versions of its record, its map's validation, discussions and reproductions. It opens with sign-in.
