OSCR

A dynamic risk prediction framework for Alzheimer's disease and related dementias with interpretability.

Code ↔ Paper

2 matches between paragraphs of the paper and lines of its authors' code, computed by the harvester (lexical-v1). Click a colored paragraph or line to see its counterpart.

The 2 matches
  1. [1] § Methods › Deep learning models ↔ i_gru_d.py, lines 244–365 · score 0.72 · context vector, weight matrix, summed, visit, dimension, tensor
  2. [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

  1. import torch
  2. import math
  3. import numpy as np
  4. class GRUD_cell(torch.nn.Module):
  5. def __init__(self,
  6. input_size, #take an integer, the number of input features
  7. hidden_size, #take an integer, the number of neurons in hidden layer
  8. output_size, #take an integer, the number of output features
  9. 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
  10. update_x_mean=True,
  11. non_neg=None,
  12. #bias=True, #always include bias vector for z,r,h
  13. #bidirectional=False, #not applicable in this study since in practice we won't know future data points
  14. #dropout_type='mloss',
  15. dropoutratio=0, #the dropout ratio for between timesteps of hidden layers.
  16. dtypearg=torch.float64,
  17. usedevice='cuda:0'
  18. ):
  19. torch.set_default_dtype(dtypearg)
  20. cuda_available = torch.cuda.is_available() #return True if NVIDIA available
  21. device = torch.device(usedevice if cuda_available else 'cpu')
  22. self.dtype=dtypearg
  23. self.device=device
  24. super(GRUD_cell,self).__init__()
  25. self.input_size=input_size
  26. self.hidden_size=hidden_size
  27. self.output_size=output_size
  28. #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
  29. if update_x_mean:
  30. self.x_mean = torch.nn.Parameter(torch.tensor(x_mean.clone(), dtype=dtypearg, requires_grad=True, device=device))
  31. else:
  32. self.register_buffer('x_mean', torch.tensor(x_mean.clone(), dtype=dtypearg, device=device))
  33. #self.bias=bias
  34. self.dropoutratio=dropoutratio
  35. 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
  36. #self.dropout_type=dropout_type
  37. #self.bidirectional=bidirectional
  38. #num_directions=2 if bidirectional else 1
  39. #weight matrix for gamma
  40. self.w_dg_x=torch.nn.Linear(input_size,input_size,bias=True,device=device)
  41. self.w_dg_h=torch.nn.Linear(input_size,hidden_size,bias=True,device=device)
  42. #weight matrix for z, update gate
  43. self.w_xz=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
  44. self.w_hz=torch.nn.Linear(hidden_size,hidden_size,bias=False,device=device)
  45. self.w_mz=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
  46. self.b_z=torch.nn.Parameter(torch.tensor(np.ndarray(hidden_size),dtype=dtypearg,requires_grad=True,device=device))
  47. #weight matrix for r, reset gate
  48. self.w_xr=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
  49. self.w_hr=torch.nn.Linear(hidden_size,hidden_size,bias=False,device=device)
  50. self.w_mr=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
  51. self.b_r=torch.nn.Parameter(torch.tensor(np.ndarray(hidden_size),dtype=dtypearg,requires_grad=True,device=device))
  52. #weight matrix for hidden units
  53. self.w_xh=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
  54. self.w_hh=torch.nn.Linear(hidden_size,hidden_size,bias=False,device=device)
  55. self.w_mh=torch.nn.Linear(input_size,hidden_size,bias=False,device=device)
  56. self.b_h=torch.nn.Parameter(torch.tensor(np.ndarray(hidden_size),dtype=dtypearg,requires_grad=True,device=device))
  57. #weight matrix for linking hidden layer to final output
  58. self.w_hy=torch.nn.Linear(hidden_size,output_size,bias=True,device=device)
  59. self.batchnorm=torch.nn.BatchNorm1d(hidden_size)
  60. #the hidden state vector, it is the conveying belt that transfer information to the next timestep
  61. #it will be scaled to batch_size x hidden_size during forward implementation (as the batch_size is unknown here)
  62. Hidden_State=torch.zeros(hidden_size,dtype=dtypearg,device=device)
  63. #it is registered in buffer, which prevents it from change. so this is a "stateless" model, which assumes no relatinoship between each training sample
  64. self.register_buffer('Hidden_State',Hidden_State)
  65. #a vector containing the last observation
  66. #it will be scaled to [batch_size, input_size] in the following script
  67. X_last_obs=torch.zeros(input_size,dtype=dtypearg,device=device)
  68. self.register_buffer('X_last_obs',X_last_obs)
  69. self.non_neg=non_neg
  70. self.reset_parameters()
  71. def reset_parameters(self): #this reset all weight parameters
  72. stdv=1.0/math.sqrt(self.hidden_size)
  73. #for weight in self.parameters(): #note this reset all weights matrix except those registered in buffer
  74. #torch.nn.init.uniform_(weight, -1 * stdv, stdv)
  75. for name,weight in self.named_parameters():
  76. if(name != 'x_mean'): #avoid reset certain weights at the startup
  77. torch.nn.init.uniform_(weight, -1 * stdv, stdv)
  78. def setdevice(self,usedevice):
  79. self.to(usedevice)
  80. self.device=torch.device(usedevice)
  81. @property
  82. def _flat_weights(self): #no idea what this is doing
  83. return list(self._parameters.values())
  84. def forward(self, input):
  85. #determine the device where
  86. device=self.device #<class 'torch.device'>
  87. #input has these dimensions [batch_size,"X Mask Delta",feature_size, timestep_size]
  88. #move input to corresponding device
  89. if(input.device != device):
  90. input=input.to(device)
  91. X=input[:,0,:,:] #X has dimension [batch_size, feature_size, timestep_size]
  92. Mask=input[:,1,:,:]
  93. Delta=input[:,2,:,:]
  94. step_size=X.size(2) #the size of timesteps
  95. output=None
  96. h=getattr(self,'Hidden_State') #h is a vector
  97. x_mean=getattr(self,'x_mean')
  98. x_last_obsv=getattr(self,'X_last_obs')
  99. #an empty tensor for holding the output of each timestep
  100. #the dimensions are [batch_size,timestep_size,output_feature_size]
  101. output_tensor=torch.empty([X.size(0),X.size(2),self.output_size],
  102. dtype=X.dtype, device=device)
  103. #an empty tensor for holding the hidden state of each timestep
  104. #the dimensions are [batch_size,timestep_size,hidden_size]
  105. hidden_tensor=torch.zeros([X.size(0),X.size(2),self.hidden_size],
  106. dtype=X.dtype,device = device)
  107. #x_tensor holds the original x values, and if not available, those imputed on the fly
  108. #[batch_size, feature_size, timestep_size]
  109. x_tensor=torch.zeros([X.size(0),self.input_size,X.size(2)],
  110. dtype=X.dtype,device = device)
  111. #iterate over timesteps
  112. for timestep in range(X.size(2)):
  113. #squeeze drop the timestep dimension and ends up with [batch_size,feature_size]
  114. x=X[:,:,timestep] #x has dimension [batch_size, feature_size]
  115. m=Mask[:,:,timestep]
  116. d=Delta[:,:,timestep]
  117. #the gamma vector for filtering x. the dimension is [batch_size,feature_size]
  118. #each element in gamma_x is a value within (0,1]
  119. gamma_x=torch.exp(-1*torch.nn.functional.relu(self.w_dg_x(d)))
  120. #the gamma vector for filtering h. the dimension is [batch_size,hidden_size]
  121. gamma_h=torch.exp(-1*torch.nn.functional.relu(self.w_dg_h(d)))
  122. #update x_last_obsv. x_last_obsv is a vector of input_size
  123. #it is changed to a [batch_size,feature_size] tensor on first run
  124. #x_last_obsv are all 0 at the beginning, and update to the latest available data
  125. x_last_obsv=torch.where(m>0,x,x_last_obsv)
  126. #set nan in x to 0 -- necessary to do this on the training data before input to cell
  127. #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
  128. x=m*x + (1-m)*(gamma_x*x_last_obsv + (1-gamma_x)*x_mean)
  129. if(not self.non_neg is None):
  130. x[:,self.non_neg]=torch.nn.functional.softplus(x[:,self.non_neg],beta=100) #when beta is 100, x=0 map to 0.007
  131. #
  132. h=gamma_h*h #h is initialized as a vector, and reshaped to [batch_size,hidden_size] on first run
  133. #z is [batch_size, hidden_size]
  134. z=torch.sigmoid(self.w_xz(x) + self.w_hz(h) + self.w_mz(m) + self.b_z)
  135. #r is [batch_size, hidden_size]
  136. r=torch.sigmoid(self.w_xr(x) + self.w_hr(h) + self.w_mr(m) + self.b_r)
  137. #h_tilde is [batch_size, hidden_size]
  138. h_tilde=torch.tanh(self.w_xh(x) + self.w_hh(r*h) + self.w_mh(m) + self.b_h)
  139. #h is [batch_size, hidden_size]
  140. h=(1-z)*h + z*h_tilde
  141. #if using batch normalization, should be added here
  142. h=self.batchnorm(h)
  143. if(self.dropoutratio > 0): #remember to use eval() to turn off dropout during evaluation
  144. h=self.dropoutlayer(h)
  145. step_output=self.w_hy(h) #[batch_size,output_size]
  146. step_output=torch.sigmoid(step_output)
  147. #note the following operations generate hard copy of the right operand, which is important
  148. output_tensor[:,timestep,:]=step_output #[batch_size,timestep_size,output_size]
  149. hidden_tensor[:,timestep,:]=h #[batch_size,timestep_size,hidden_size]
  150. x_tensor[:,:,timestep]=x #[batch_size, feature_size, timestep_size]
  151. #end of for
  152. output=(output_tensor,hidden_tensor,x_tensor,Mask)
  153. return output
  154. #end of forward
  155. #end of class
  156. class GRUD_model(torch.nn.Module):
  157. def __init__(self,
  158. input_size,
  159. hidden_size, #this determines hidden_size of both the first and all stacked layers
  160. output_size, #the output size of every layer
  161. num_layers=1,
  162. x_mean=0, #empirical mean of each input feature
  163. update_x_mean=True,
  164. non_neg=None,
  165. #bias=True,
  166. #batch_first=False,
  167. #bidirectional=False,
  168. #dropout_type='mloss',
  169. dropoutratio=0,
  170. dtypearg=torch.float64,
  171. usedevice='cuda:0'
  172. ):
  173. torch.set_default_dtype(dtypearg)
  174. cuda_available = torch.cuda.is_available()
  175. device = torch.device(usedevice if cuda_available else 'cpu')
  176. self.device=device
  177. super(GRUD_model,self).__init__()
  178. #first layer is the GRU-D
  179. self.gru_d=GRUD_cell(input_size=input_size,
  180. hidden_size=hidden_size,
  181. output_size=output_size,
  182. x_mean=x_mean,
  183. update_x_mean=update_x_mean,
  184. non_neg=non_neg,
  185. dropoutratio=dropoutratio,
  186. dtypearg=dtypearg,
  187. usedevice=usedevice
  188. )
  189. self.num_layers=num_layers
  190. self.hidden_size=hidden_size
  191. #stack other layers as regular GRU layer
  192. if(self.num_layers > 1):
  193. self.gru_layers=torch.nn.GRU(input_size=hidden_size,
  194. hidden_size=hidden_size,
  195. 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
  196. num_layers=self.num_layers-1,
  197. dropout=dropoutratio,
  198. device=device
  199. ) #I manually confirmed torch.nn.GRU dropout is sensitive to switch between eval() and training()
  200. #this is for converting the last layer hidden output to actual output
  201. self.hidden_to_output=torch.nn.Linear(hidden_size, output_size, bias=True)
  202. #end of __init__
  203. def setdevice(self,usedevice):
  204. self.to(usedevice)
  205. self.device=torch.device(usedevice)
  206. self.gru_d.setdevice(usedevice)
  207. def _flat_weights(self):
  208. return list(self._parameters.values())
  209. def forward(self,input):
  210. #pass through the first gru_d layer
  211. #determine the device where
  212. if(hasattr(self,'device')):
  213. device=self.device #<class 'torch.device'>
  214. #move input to corresponding device
  215. if(input.device != device):
  216. input=input.to(device)
  217. (real_output,hidden,x_tensor,m_tensor)=self.gru_d(input)
  218. #real_output is [batch_size,timestep,output_feature]
  219. #hidden is [batch_size,timestep,hidden_size]
  220. #x_tensor [batch_size, feature_size, timestep_size]
  221. if(self.num_layers > 1):
  222. #pass through the rest of regular gru layers
  223. #output contains the final layer output for all samples and all timesteps
  224. #output has shape [batch_size,timestep,hidden_size]
  225. #hidden has shape [num_layers, batch_size, hidden_size]
  226. #in this example output[:,timestep-1,:] == hidden, should be all true
  227. (output,hidden)=self.gru_layers(hidden)
  228. #covert output of last layer hidden output [batch_size, timestep, hidden_size] to real output [batch_size, timestep, output_feature]
  229. real_output=self.hidden_to_output(output)
  230. real_output=torch.sigmoid(real_output)
  231. hidden=output #hidden now is [batch_size, timestep, hidden_size]
  232. #end of if
  233. return(real_output,hidden,x_tensor,m_tensor) #make sure the output shape is same as gru_d
  234. #end of forward
  235. #end of class
  236. class I_GRUD_model(torch.nn.Module):
  237. def __init__(self,
  238. input_size, #the input feature size
  239. hidden_size, #this determines hidden_size of both the first and all stacked layers
  240. output_size, #the output size of every layer
  241. num_layers=1,
  242. x_mean=0, #empirical mean of each input feature
  243. update_x_mean=True,
  244. #bias=True,
  245. #bidirectional=False,
  246. dropoutratio=0,
  247. betadropoutratio=0,
  248. traceback=200,
  249. non_neg=None,
  250. dtypearg=torch.float64,
  251. interpretable_mode=True, #whether turn on interpretable mode
  252. usedevice='cuda:0'
  253. ):
  254. torch.set_default_dtype(dtypearg)
  255. cuda_available = torch.cuda.is_available() #return True if NVIDIA available
  256. device = torch.device(usedevice if cuda_available else 'cpu')
  257. self.dtype=dtypearg
  258. self.device=device
  259. super(I_GRUD_model,self).__init__()
  260. self.input_size=input_size
  261. self.hidden_size=hidden_size
  262. self.output_size=output_size
  263. self.traceback=traceback
  264. self.interpretable_mode=interpretable_mode
  265. self.batchnorm=torch.nn.BatchNorm1d(input_size)
  266. self.grud_model=GRUD_model(input_size,hidden_size,output_size,
  267. num_layers=num_layers,x_mean=x_mean,
  268. update_x_mean=update_x_mean,non_neg=non_neg,
  269. dropoutratio=dropoutratio,dtypearg=dtypearg,
  270. usedevice=usedevice)
  271. if(self.interpretable_mode):
  272. #this following codes are for attention
  273. self.w_beta=torch.nn.Linear(hidden_size,input_size,bias=False,device=device) #feature wise attention
  274. self.w_epsilon=torch.nn.Linear(hidden_size, 1, bias=False, device=device) #timestep wise attention
  275. self.softmax=torch.nn.Softmax(dim=1)
  276. #weight matrix for linking context vector (attention version only) to final output
  277. self.w_cy=torch.nn.Linear(input_size,output_size,bias=True,device=device)
  278. self.betadropoutlayer=torch.nn.Dropout(p=betadropoutratio)
  279. #end of if
  280. self.reset_parameters()
  281. def reset_parameters(self): #this reset all weight parameters
  282. stdv=1.0/math.sqrt(self.hidden_size)
  283. #for weight in self.parameters(): #note this reset all weights matrix except those registered in buffer
  284. #torch.nn.init.uniform_(weight, -1 * stdv, stdv)
  285. for name,weight in self.named_parameters():
  286. if(name != 'x_mean'): #avoid reset certain weights at the startup
  287. torch.nn.init.uniform_(weight, -1 * stdv, stdv)
  288. def setdevice(self,usedevice):
  289. self.to(usedevice)
  290. self.device=torch.device(usedevice)
  291. self.grud_model.setdevice(usedevice)
  292. @property
  293. def _flat_weights(self): #no idea what this is doing
  294. return list(self._parameters.values())
  295. def forward(self, input):
  296. #determine the device where
  297. device=self.device #<class 'torch.device'>
  298. #move input to corresponding device
  299. if(input.device != device):
  300. input=input.to(device)
  301. (real_output,hidden,x_tensor,m_tensor)=self.grud_model(input)
  302. #real_output [batch_size,timestep,output_size]
  303. #hidden [batch_size, timestep, hidden_size]
  304. if(not self.interpretable_mode):
  305. return (real_output,hidden,x_tensor,m_tensor)
  306. else:
  307. batch_size=real_output.size(0)
  308. timestep_size=real_output.size(1)
  309. #an empty tensor for holding the output of each timestep
  310. #the dimensions are [batch_size,timestep_size,output_feature_size]
  311. output_tensor=torch.empty([batch_size,timestep_size,self.output_size],
  312. dtype=input.dtype, device=device)
  313. context_tensor=torch.empty([batch_size,timestep_size,self.output_size],
  314. dtype=input.dtype, device=device)
  315. #visit level attention has two parts
  316. #epsilon_tensor is for holding original output [batch_size,timestep_size]
  317. #alpha_tensor is softmax activation of epsilon, which provides timestep level attention
  318. epsilon_tensor=torch.zeros([batch_size,timestep_size],
  319. dtype=input.dtype,device = device)
  320. #feature level attention [batch,input,timestep]
  321. beta_tensor=torch.zeros([batch_size,self.input_size,timestep_size],
  322. dtype=input.dtype,device = device)
  323. #iterate over timesteps
  324. for timestep in range(timestep_size):
  325. h=hidden[:,timestep,:] #[batch, hidden]
  326. #********************************************#
  327. #the following codes are RETAIN-like attention mechanism
  328. #visit level attention
  329. epsilon=self.w_epsilon(h) #[batch,1]
  330. epsilon_tensor[:,timestep]=epsilon[:,0] #store epsilon for each timestep
  331. #softmax epsilon to get alpha attention
  332. attentionsta=timestep - self.traceback #consider limited timesteps back
  333. if(attentionsta < 0):attentionsta=0
  334. alpha=self.softmax(epsilon_tensor[:,attentionsta:(timestep+1)].clone()) #[batch,ts]
  335. #convert [batch,ts] to [batch,input,ts] for easy muliplication with beta and x
  336. alpha=alpha[:,None,:] #convert to [batch,1,ts] note! reference only
  337. 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
  338. #feature level attention
  339. beta=self.w_beta(h) #[batch,input_size]
  340. beta=5 * torch.tanh(beta/5) #smooth out beta as I found beta maybe super large in certain training fold
  341. beta=self.betadropoutlayer(beta)
  342. #beta=self.batchnorm(beta)
  343. beta_tensor[:,:,timestep]=beta #store beta [batch,input,ts]
  344. #multiply alpha, beta, and x for all current timesteps
  345. #!!! clone is necessary
  346. abx=torch.mul(alpha,torch.mul(beta_tensor[:,:,attentionsta:(timestep + 1)].clone(),x_tensor[:,:,attentionsta:(timestep + 1)].clone())) #[batch,input,ts]
  347. #context is weighted (already done) average (or sum) of traceback timesteps
  348. context=torch.sum(abx,dim=2) #average existing timesteps values for each input feature [batch,input]
  349. #step_output=torch.sigmoid(self.w_cy(context)) #[batch,output]
  350. context_sum=torch.sum(context,dim=1) #[batch]
  351. context_tensor[:,timestep,:]=context_sum[:,None] #[batch,timestep,output]
  352. step_output=torch.sigmoid(context_sum) #[batch]
  353. step_output=step_output[:,None] #[batch,output]
  354. output_tensor[:,timestep,:]=step_output #[batch,timestep,output]
  355. #end of for
  356. output=(output_tensor,hidden,epsilon_tensor,beta_tensor,x_tensor,context_tensor,m_tensor)
  357. return output
  358. #end of if
  359. #end of forward
  360. #end of class
  361. from torch.utils.data import Dataset
  362. from torch.utils.data import DataLoader
  363. class datasetgenerator(Dataset):
  364. def __init__(self,nparray,targetarray,valid_timesteps_array=None):
  365. assert(nparray.shape[0] == targetarray.shape[0]), "nparray length and target array length not match"
  366. self.z=None
  367. if(not valid_timesteps_array is None):
  368. assert(nparray.shape[0] == len(valid_timesteps_array)), "nparray length and valid timestep array length not match"
  369. self.z=valid_timesteps_array
  370. self.x=nparray
  371. self.y=targetarray
  372. def __len__(self):
  373. return(self.x.shape[0])
  374. def __getitem__(self,idx):
  375. if(self.z is None):
  376. return(self.x[idx,:,:,:],self.y[idx])
  377. else:
  378. return(self.x[idx,:,:,:],self.y[idx],self.z[idx])
  379. #end of if
  380. #end of def
  381. #end of class

i_gru_d.py at commit 82a40e7, under other · at the source

Overview

Authors: Xiaoyang Ruan1, Shuyu Lu1, Sunyang Fu1, Jaerong Ahn1, Fang Chen1, Rui Li1, Andrew Wen1, Liwei Wang2, Ezenwa Onyema3, Victoria Tang3, Hongfang Liu1
  1. Department of Health Data Science and AI, McWilliams School of Biomedical Informatics, UT Health Houston, Houston, USA
  2. Department of Clinical and Health Informatics, McWilliams School of Biomedical Informatics, UT Health Houston, Houston, USA
  3. Department of Internal Medicine, McGovern Medical School, UT Health Houston, Houston, USA
Journal: NPJ digital medicine, volume 9, issue 1, article 446
Dates: received 8 October 2025; accepted 30 April 2026; published online 15 May 2026
Type: Research article · Language: English
License: CC BY-NC-ND
Identifiers: DOI 10.1038/s41746-026-02732-0 · PMID 42141052 · PMCID PMC13260447 · OpenAlex W7161287886
Open access: gold, a free copy (OpenAlex)
Status: code verified
Categories: Alzheimer's / dementia (population), clinical / translational (subfield)
Methods: Statistics, Machine learning, Preprocessing
Keywords: Computational biology and bioinformatics, Diseases, Health care, Mathematics and computing, Medical research, Neurology
Topic: Machine Learning in Healthcare (Artificial Intelligence, Computer Science), according to OpenAlex
Funding: National Human Genome Research Institute (3R01HG012748-02S2); Cancer Prevention and Research Institute of Texas Established Investigator Award (RR230020); National Institute of Aging (R01AG072799-03)
Citations: not cited yet (Europe PMC); 66 references in the paper

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

License: MIT
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 861fc49d9595424bd7604c5cb1a39ab6c500f652, 25 July 2022
Languages: Python (7), Jupyter (3)
Size: 17 files, 10 scripts
Software Heritage: not archived
Found in: “Code availability”
Holds: README, license file, environment (requirements.txt), 3 notebooks
Not found: CITATION.cff, tests, continuous integration, documentation
Tools: Keras (6 files), NumPy (5 files), scikit-learn (2 files), TensorFlow (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
12 files

ruanxiaoyang-ut/gru-d-retain

License: other
State: the link answers, verified on 28 September 2026
Evidence: files inventoried
Commit: 82a40e73b2a51b7f7a0e30b49ba56b0b0750c67f, 6 October 2025
Languages: Python (1)
Size: 3 files, 1 script
Software Heritage: not archived
Found in: “Code availability”
Holds: README, license file
Not found: CITATION.cff, environment file, tests, continuous integration, documentation
Tools: NumPy (1 file), PyTorch (1 file)
Availability: 1 check, the latest on 28 September 2026: the link answers
  • 28 September 2026: the link answers
3 files

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:

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:

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://doi.org/10.1038/s41746-026-02732-0

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/s41746-026-02732-0},
url = {https://doi.org/10.1038/s41746-026-02732-0},
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/05/15
VL - 9
IS - 1
SP - 446
SN - 2398-6352
PB - Nature Publishing Group
DO - 10.1038/s41746-026-02732-0
UR - https://doi.org/10.1038/s41746-026-02732-0
LA - en
ER -

CSL-JSON

{
"id": "10.1038/s41746-026-02732-0",
"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": "NPJ Digit Med",
"volume": "9",
"issue": "1",
"page": "446",
"DOI": "10.1038/s41746-026-02732-0",
"PMID": "42141052",
"PMCID": "PMC13260447",
"ISSN": "2398-6352",
"publisher": "Nature Publishing Group",
"URL": "https://doi.org/10.1038/s41746-026-02732-0",
"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 neurology
In 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 reports
In 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 biology
In 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 advances
In 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 advances
In 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 biology
In 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 Society
In 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 biology
In 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 express
In 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.

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.